diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/ControlStructures.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/ControlStructures.md index f06e2a07a6d..a6457cb4aac 100644 --- a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/ControlStructures.md +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/ControlStructures.md @@ -569,9 +569,7 @@ Um "default" (padrão) é equivalente a um "else", ele realiza algo caso todos o ### goto -goto is essentially a jump, it goes to a label without question (i.e. there's no condition to need to be true). You can see an example above in the if-goto loop. - -Um "goto" (vá até) é essencialmente um pulo, ele vai até determinada "label" sem a necessidade de uma condição. Você pode ver em um exemplo acima um if-goto loop. +Um `goto` é essencialmente um salto: ele leva a execução diretamente para um rótulo, sem verificar condições (como no exemplo do loop `if-goto` mostrado acima). ```c goto my_label; diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Directives.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Directives.md index 766a37f2e39..fa4015f5766 100644 --- a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Directives.md +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Directives.md @@ -1,24 +1,28 @@ --- -title: "Diretivas" -sidebar_label: "Diretivas" +title: "Palavras-chave: diretivas" +sidebar_label: "Palavras-chave: diretivas" --- -As diretivas são instruções passadas ao compilador para controlar como ele interpreta seu código fonte. +Diretivas são instruções passadas ao compilador para controlar como ele interpreta o código-fonte. + ## `#assert` -Isto verifica se a expressão constante é verdadeira e se não aborta a compilação. +Verifica se uma expressão constante é verdadeira; caso contrário interrompe a compilação. + ```c #define MOO 10 #assert MOO > 5 ``` -Isso irá compilar corretamente. +Compila normalmente. + ```c #define MOO 1 #assert MOO > 5 ``` -Isso não vai dar e dará um erro fatal. Isto é semelhante a: +Provoca um erro fatal. É semelhante a: + ```c #define MOO 1 #if MOO <= 5 @@ -26,234 +30,196 @@ Isso não vai dar e dará um erro fatal. Isto é semelhante a: #endif ``` -No entanto, a afirmação dará um erro: +A diferença é que `#assert` mostra a mensagem: + ``` Assertation failed: 1 > 5 ``` -Onde o segundo dará um erro: +enquanto o segundo exibe: + ``` User error: Moo check failed ``` -O que pode ou não ser útil. ## `#define` -`#define` é uma diretiva de substituição de texto, onde quer que o primeiro símbolo da definição seja encontrado, o resto será colocado. +`#define` substitui texto: onde o símbolo declarado aparece, ele é trocado pelo conteúdo definido. ```c #define MOO 7 printf("%d", MOO); ``` -Será mudado para: +Vira: ```c printf("%d", 7); ``` -É por isso que todas as definições se perdem na descompilação, pois não existem quando o código é compilado (todas as diretivas são pré-processadas). As definições não têm que conter números: +Por isso `#define`s não existem no código compilado; o pré-processador resolve tudo antes. Eles não precisam conter números: + ```c #define PL new i = 0; i < MAX_PLAYERS; i++) if (IsPlayerConnected(i) -for(PL) printf("%d connected", i); +for (PL) printf("%d connected", i); ``` -Compilará para o loop de 1000, mais conhecido como Player Loop que todos nós conhecemos e amamos(e desprezamos). Observe como os parênteses são usados aqui, alguns do loop 'for' e outros do macro definido (o substituto). +O código acima gera o clássico player loop. Note como parte dos parênteses vem do `for` e parte do macro. + +Pouca gente sabe que você pode criar definições multilinha escapando a quebra de linha: -Outro fato pouco conhecido sobre as definições é que elas podem ser multi-linhas se você pular da nova linha. Geralmente uma nova linha termina a definição, no entanto, o seguinte é válido: ```c #define PL \ new i = 0; i < MAX_PLAYERS; i++) \ if (IsPlayerConnected(i) - -printf("%d", MOO(6)); -``` - -Isso resultará em 42 (não, não é escolhido aleatoriamente). Notou os parênteses excessivos na definição? Isto porque as definições são substituições de texto reto, de modo que serão compiladas como: -```c -printf("%d", ((6) * 7)); ``` -Está bem assim, mas olhe este exemplo: -```c -printf("%d", MOO(5 + 6)); -``` - -Você esperaria que compilasse para produzir 77 ((5 + 6) \* 7) e com os parênteses, porém sem os parênteses que você tem: +Macros também podem receber parâmetros: ```c #define MOO(%0) \ - %0 * 7 + ((%0) * 7) -printf("%d", MOO(5 + 6)); +printf("%d", MOO(6)); // imprime 42 +printf("%d", MOO(5 + 6)); // imprime 77 ``` -O que converte para: - -```c -printf("%d", MOO(5 + 6 * 7)); -``` +Os parênteses extras são importantes. Sem eles `MOO(5 + 6)` se expandiria para `5 + 6 * 7`, que resulta em 47 devido à ordem das operações. -Que, devido à ordem de operações, compila como (5 + (6 \* 7)), o que se for 47 e é muito errado. +Se o número de argumentos fornecido for maior que o declarado, o último parâmetro recebe todos os valores restantes: -Um fato interessante sobre os parâmetros é que, se você tem muitos, o último será todos os parâmetros extras. Assim formando: ```c #define PP(%0,%1) \ printf(%0, %1) -PP(%s %s %s, "hi", "hello", "hi"); +PP(%s %s %s, "hi", "hello", "hi"); // imprime "hi hello hi" ``` -Irá imprimir de fato: - -``` -hi hello hi -``` - -Como `%1` contém "hi", "hello", "hi". Você também deve ter notado o uso de `#` para converter um literal em uma string. Esta é uma característica apenas do SA-MP e pode ser útil. Foi apenas adicionado aqui para dar uma distinta distinção entre os parâmetros. +O operador `#` transforma um literal em string; esse recurso é específico do SA-MP. ## `#else` -`#else` É igual ao `else` comum, só que na diretiva #else. +Equivalente a `else`, mas usado em blocos condicionais do pré-processador (`#if`). ## `#elseif` -`#elseif` É igual elseif comum, só que na diretiva #if. +Versão de `else if` para `#if`: ```c #define MOO 10 #if MOO == 9 - printf("if"); + printf("if"); #elseif MOO == 8 - printf("else if"); + printf("else if"); #else - printf("else"); + printf("else"); #endif ``` ## `#emit` -Esta diretiva não está listada na tabela pawn-lang.pdf, entretanto, ela existe. Ela é basicamente um compilador em linha. Se você conhece AMX, você pode usar isto para colocar os opcodes AMX diretamente em seu código. A única limitação é que isso permite apenas um argumento. Sintaxe: `#emita >argumento>`. `` pode ser um número racional, inteiro ou símbolo (local ou global) (variáveis, funções e rótulos). A lista de opcodes e seu significado pode ser encontrada em Pawn Toolkit ver. 3664. + +Não aparece na tabela do `pawn-lang.pdf`, mas existe. É basicamente um compilador inline: se você conhece AMX, pode inserir opcodes diretamente no código. Sintaxe: `#emit `. O argumento pode ser um número (inteiro ou racional) ou um símbolo local/global (variáveis, funções ou rótulos). Consulte o Pawn Toolkit v3664 para a lista de opcodes. + ## `#endif` -`#endif` É como se um aparelho para-se. #if não usar aparelho, tudo é somado condicionalmente até o correspondente #endif. +Funciona como a chave de fechamento de um `if`. Tudo o que estiver entre `#if` e `#endif` é incluído condicionalmente. -## `#endinput, #endscript` +## `#endinput`, `#endscript` -Isto impede a inclusão de um único arquivo. +Interrompem a inclusão do arquivo atual. ## `#error` -Isto serve para o compilador instantaneamente imprimir mensagem de erro personalizada. Veja #assert para um exemplo. +Interrompe a compilação imediatamente exibindo uma mensagem customizada. Veja `#assert` para um exemplo. ## `#if` -`#if` Indica para o pré-processador e se é para compilar aquele trecho de código. Pode escolher exatamente o que compilar e o que não compilar a partir daqui. Por exemplo, considere o seguinte código: +Faz para o pré-processador o que `if` faz para o código: permite escolher o que será compilado. Considere: ```c -#define LIMITE 10 +#define LIMIT 10 -if (LIMITE < 10) +if (LIMIT < 10) { - printf("Limite muito baixo"); + printf("Limit too low"); } ``` -That will compile as: +Isso vira: ```c if (10 < 10) { - printf("Limite muito baixo"); + printf("Limit too low"); } ``` -O que claramente nunca retornará verdadeiro e o compilador sabe disso - portanto retornará um aviso de "expressão constante". A questão é, se nunca será verdade, de que vale a pena incluí-lo de todo? Poderá simplesmente remover o código, mas depois não haverá verificações se alguém alterar o macro LIMITE e recompilar. É para isto que serve a diretiva #if. Ao contrário do normal, que dá um aviso se a expressão for constante, as expressões #if devem ser constantes. Portanto: +O compilador sabe que a condição é constante e avisa. Se o resultado nunca muda, por que manter o código? Remover o trecho impediria validações futuras caso `LIMIT` fosse alterado. É aí que `#if` ajuda: diferente de `if`, ele *espera* expressões constantes. + ```c -#define LIMITE 10 +#define LIMIT 10 -#if LIMITE < 10 - #error Limite muito baixo +#if LIMIT < 10 + #error Limit too low #endif ``` -Isso irá verificar que o limite não é demasiado pequeno quando se compila e se é, dará um erro de tempo de compilação, em vez de se ter de testar o modo para ver se há algo de errado. Isto também significa que não é gerado um excesso de código. Note também a falta de parênteses, pode utilizá-los, e pode precisar deles em expressões mais complexas, mas não são necessários. - -Aqui está outro exemplo: - -```c -#define LIMITE 10 +Assim a checagem ocorre na compilação e evita código morto. Note que os parênteses são opcionais. -if (LIMITE < 10) -{ - printf("Limite menor que 10"); -} -else -{ - printf("Limite igual ou menor do que 10"); -} -``` +Outro exemplo: -Mais uma vez, esta é uma verificação constante, que dará um aviso, mas ambas as impressões serão compiladas quando sabermos que apenas uma será executada. Usando #if isto se tornar: ```c -#define LIMITE 10 +#define LIMIT 10 -#if LIMITE < 10 - printf("Limite menor que 10"); +#if LIMIT < 10 + printf("Limit less than 10"); #else - printf("Limite igual ou menor que 10"); + printf("Limit equal to or above 10"); #endif ``` -Dessa forma, apenas a impressão que é necessária será compilada e a outra ainda estará no seu código-fonte, caso alterem o valor do macro LIMITE e recompilem, mas não será incluída no código, uma vez que não é necessária. Esta forma também significa o inútil se não for executado sempre que o seu código for executado, o que é sempre bom. +Somente o ramo válido é compilado, mas o código redundante permanece no arquivo para futuras mudanças. ## `#include` -Isto retira todo o código de um arquivo especificado e insere-o no seu código no ponto em que a linha include se encontra. Há dois tipos de include: relativo e sistema (termos inventados pelo autor para simplificar o que está sendo feito). Relativo inclui usar aspas duplas em torno do nome do arquivo e estão localizados em relação ao arquivo atual, portanto: +Insere o conteúdo de outro arquivo no ponto onde a diretiva aparece. Existem dois formatos: -```c -#include "include.pwn" -``` +- **Relativo** (`"arquivo.pwn"`): o caminho é resolvido relativo ao arquivo atual. +- **Sistema** (``): o arquivo é buscado no diretório `include` ao lado (ou um nível acima) do compilador Pawn, como `pawno/include`. -incluiria o arquivo "include.pwn" do mesmo diretório que o arquivo incluindo esse arquivo. O outro tipo, sistema, inclui o arquivo do diretório "include" que está localizado ou no mesmo diretório que o compilador Pawn ou diretório pai (caminhos: "include",".../include"): -```c -#include "" -``` - -Incluiria o arquivo "include.inc" (note a falta de extensão, pode especificar se um arquivo não for .p (não .pwn ou .inc) do diretório pawno/include (supondo que você esteja utilizando Pawno). - -Ambos os tipos podem conter diretórios: - -```#include +#include "folder/me.pwn" +#include ``` -Ambos incluirão um arquivo de um diretório abaixo dos respectivos directórios por defeito. Se o arquivo não existir, a compilação falhará. +Se o arquivo não existir, a compilação falha. ## `#pragma` -Esta é uma das diretivas mais complexas. Ela tem uma série de opções para controlar como seu roteiro funciona. Um exemplo de configuração pareceria: +Diretiva para ajustar o comportamento do compilador. Exemplo: + ```c #pragma ctrlchar '$' ``` -Mais uma vez, esta é uma verificação constante, que dará um aviso, mas ambas as impressões serão compiladas quando sabermos que apenas uma será executada. Usando #if isto se tornar: +Isso altera o caractere de escape de `\` para `$`. Algumas opções úteis no contexto SA:MP: + | Nome | Valores | Descrição | -| ---------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| codepage | nome/valor | Define a página de codificação Unicode a utilizar para cordas. | -| comprimir | 1/0 | Sem suporte no SA-MP - não tente usá-lo. | -| depreciado | símbolo | gerou um aviso se o símbolo dado for utilizado para dizer às pessoas que há uma versão melhor disponível. | -| dinâmico | valor (geralmente uma potência de 2) | Define o tamanho da memória (em células) atribuída à pilha e à pilha. Necessário se receber um aviso de utilização de memória em excesso após a compilação. (Uma tabela estranha após a linha de copyright do compilador) | -| biblioteca | nome dll | Widley incorrectamente utilizado no SA-MP. Isto especifica a dll para obter as funções nativas definidas no ficheiro de onde é proveniente. Não define um ficheiro **como** uma biblioteca. | -| pack | 1/0 | Troque os significados de !"" e """. Ver penhor-lang.pdf para mais informações sobre cordas embaladas. | -| tamanho do separador | valor | Outra configuração largamente mal utilizada. Isto deve ser utilizado para definir o tamanho de um separador para evitar avisos de compilação que estejam errados devido a espaços e separadores serem utilizados alternadamente. Isto é definido para 4 em SA:MP, pois é o tamanho de uma tabulação em pawno. Definindo este valor como 0 irá suprimir todos os seus avisos de indentação, mas é altamente desaconselhável uma vez que permite um código totalmente ilegível. | -| não utilizado | símbolo | como depreciado isto aparece após o símbolo para o qual se deseja suprimir o aviso "símbolo nunca é utilizado". Geralmente, o método preferido para o fazer é a utilização de stock, no entanto, isto nem sempre é aplicável (por exemplo, os parâmetros da função não podem ser compilados). - -### Descontinuado +| --- | --- | --- | +| `codepage` | nome/valor | Define a página de código Unicode usada para strings. | +| `compress` | `1`/`0` | Não suportado no SA-MP. | +| `deprecated` | símbolo | Gera um aviso ao usar o símbolo indicado, sinalizando que existe versão melhor. | +| `dynamic` | valor (geralmente potência de 2) | Define o tamanho (em células) da pilha e do heap; use ao receber avisos de memória insuficiente. | +| `library` | nome da DLL | Indica a DLL da qual funções nativas serão importadas; não transforma o arquivo atual em biblioteca. | +| `pack` | `1`/`0` | Inverte o significado de `!""` e `""`. Veja `pawn-lang.pdf` para detalhes sobre strings compactadas. | +| `tabsize` | valor | Define o tamanho do tab para evitar avisos errados de indentação; em SA:MP o padrão é 4. Não use 0, pois oculta avisos úteis. | +| `unused` | símbolo | Suprime o aviso “symbol is never used” para o símbolo indicado. Útil quando `stock` não se aplica (por exemplo, parâmetros). | + +### Depreciado ```c new @@ -261,13 +227,14 @@ new #pragma deprecated gOldVariable -main() {printf("%d", gOldVariable);} +main() { printf("%d", gOldVariable); } ``` -Isso dará um aviso de que a gOldVariable não deve mais ser usada. Isto é útil principalmente para funções que preservam a compatibilidade com o passado enquanto atualizam o API. -### `#tryinclude` +Gera um aviso indicando que `gOldVariable` não deve mais ser usada. Útil para manter compatibilidade enquanto a API evolui. + +## `#tryinclude` -Isso é semelhante a #include, mas se o arquivo não existir, a compilação não falhará. Isso é útil apenas para incluir recursos em seu script se uma pessoa tiver o plugin correto instalado(Ou pelo menos o plugin incluído) +Semelhante a `#include`, mas não falha se o arquivo não existir. Serve para incluir recursos opcionais quando o desenvolvedor possui o plugin apropriado. **myinc.inc** @@ -277,41 +244,43 @@ Isso é semelhante a #include, mas se o arquivo não existir, a compilação nã #endif #define _MY_INC_INC -stock MinhaIncludeFunc() {printf("Olá!");} +stock MyIncFunc() { printf("Hello"); } ``` -**Gamemode:** +**Gamemode** ```c -#tryinclude +#tryinclude main() { - #if defined _MINHA_INC_INC - MinhaIncludeFunc(); + #if defined _MY_INC_INC + MyIncFunc(); #endif } ``` -Isso só chamará a função MinhaIncludeFunc se o arquivo com ele for encontrado na pasta includes e compilado com sucesso. Isso, como afirmado anteriormente, é bom para plugins padrões (Por exemplo a_samp.inc ou a_actors.inc) para verificar se o desenvolvedor realmente tem o plugin instalado. -### `#undef` +`MyIncFunc` só será chamada se `myinc.inc` estiver disponível e compilar sem erros. + +## `#undef` -Remove um macro ou simbolo constante anteriormente definido. +Remove um macro ou símbolo constante previamente definido. ```c #define MOO 10 printf("%d", MOO); #undef MOO -printf("%d", MOO); +printf("%d", MOO); // erro: MOO não existe mais ``` -Irá falhar ao compilar, pois o macro MOO não existe. +Também funciona com enumeradores: + ```c enum { - e_example = 300 + E_EXAMPLE = 300 }; -printf("%d", e_example); -#undef e_example -printf("%d", e_example); // fatal error +printf("%d", E_EXAMPLE); +#undef E_EXAMPLE +printf("%d", E_EXAMPLE); // erro fatal ``` diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Initialisers.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Initialisers.md new file mode 100644 index 00000000000..2eadae0e8ef --- /dev/null +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Initialisers.md @@ -0,0 +1,340 @@ +--- +title: "Palavras-chave: inicializadores" +sidebar_label: "Palavras-chave: inicializadores" +--- + +## `const` + +```c +new const + MY_CONSTANT[] = {1, 2, 3}; +``` + +`const` não é muito usado, mas serve para declarar variáveis que não podem ser alteradas em tempo de execução. Em funções, parâmetros de array marcados como `const` podem ser otimizados, e você pode criar algo parecido com um `#define`, só que baseado em arrays. Como `const` é um modificador, ele deve acompanhar `new` ou outro declarador de variável. Se tentar modificar uma variável constante, o compilador reclamará. + +## `enum` + +Enums são úteis para agrupar dados relacionados, substituir longas listas de `#define`s e criar novas tags. Também facilitam o controle de índices de arrays. + +Definição mais comum: + +```c +enum E_MY_ARRAY +{ + E_MY_ARRAY_MONEY, + E_MY_ARRAY_GUN +} + +new + gPlayerData[MAX_PLAYERS][E_MY_ARRAY]; + +public OnPlayerConnect(playerid) +{ + gPlayerData[playerid][E_MY_ARRAY_MONEY] = 0; + gPlayerData[playerid][E_MY_ARRAY_GUN] = 5; +} +``` + +Isso cria dois slots por jogador. Sem enum, ficaria: + +```c +new + gPlayerData[MAX_PLAYERS][2]; + +public OnPlayerConnect(playerid) +{ + gPlayerData[playerid][0] = 0; + gPlayerData[playerid][1] = 5; +} +``` + +Funciona, mas é menos legível. Se quiser inserir um slot entre 0 e 1, teria de renumerar tudo. Com enum basta adicionar o novo campo: + +```c +enum E_MY_ARRAY +{ + E_MY_ARRAY_MONEY, + E_MY_ARRAY_AMMO, + E_MY_ARRAY_GUN +} +``` + +Ao recompilar, o compilador ajusta todos os índices. + +A forma completa de um enum é: + +```c +enum NAME (modificador) +{ + NAME_ENTRY_1 = valor, + ... +} +``` + +Se você não definir um modificador, o padrão é `+= 1`. Ou seja, cada item vale o item anterior + 1. Exemplo: + +```c +enum E_EXAMPLE +{ + E_EXAMPLE_0, + E_EXAMPLE_1, + E_EXAMPLE_2 +} +``` + +Aqui temos 0, 1, 2 e o valor do enum (`E_EXAMPLE`) é 3. Mudando o modificador para `+= 5` os valores passam a ser 0, 5, 10 e 15. Declarar um array com `gEnumArray[E_EXAMPLE]` reservá 15 células, mas os índices simbólicos atingem apenas 0, 5 e 10. + +Também é possível usar outro modificador, como `*= 2`. Nesse caso, se não definir o primeiro valor manualmente, todos continuam 0. Para corrigir, basta atribuir um valor inicial: + +```c +enum E_EXAMPLE (*= 2) +{ + E_EXAMPLE_0 = 1, + E_EXAMPLE_1, + E_EXAMPLE_2 +} +``` + +Resultado: 1, 2, 4 e 8. Você pode definir quantos valores quiser: + +```c +enum E_EXAMPLE (*= 2) +{ + E_EXAMPLE_0, + E_EXAMPLE_1 = 1, + E_EXAMPLE_2 +} // 0, 1, 2, 4 +``` + +Arrays também são suportados: + +```c +enum E_EXAMPLE +{ + E_EXAMPLE_0[10], + E_EXAMPLE_1, + E_EXAMPLE_2 +} // produz 0, 10, 11 e 12 +``` + +Itens podem ter tags. No exemplo inicial: + +```c +enum E_MY_ARRAY +{ + E_MY_ARRAY_MONEY, + E_MY_ARRAY_AMMO, + Float:E_MY_ARRAY_HEALTH, + E_MY_ARRAY_GUN +} +``` + +Assim evitamos `tag mismatch` ao armazenar floats. + +Enums também podem servir como tags: + +```c +enum E_MY_TAG (<<= 1) +{ + E_MY_TAG_NONE, + E_MY_TAG_VAL_1 = 1, + E_MY_TAG_VAL_2, + E_MY_TAG_VAL_3, + E_MY_TAG_VAL_4 +} + +new + E_MY_TAG:gMyTagVar = E_MY_TAG_VAL_2 | E_MY_TAG_VAL_3; +``` + +`gMyTagVar` terá valor 6 e tag personalizada. Atribuir um número puro gera aviso: + +```c +gMyTagVar = 7; // warning +gMyTagVar = E_MY_TAG:7; // válido +``` + +Muito útil para flags ou dados combinados: + +```c +enum E_MY_TAG (<<= 1) +{ + E_MY_TAG_NONE, + E_MY_TAG_MASK = 0xFF, + E_MY_TAG_VAL_1 = 0x100, + E_MY_TAG_VAL_2, + E_MY_TAG_VAL_3, + E_MY_TAG_VAL_4 +} + +new + E_MY_TAG:gMyTagVar = E_MY_TAG_VAL_2 | E_MY_TAG_VAL_3 | (E_MY_TAG:7 & E_MY_TAG_MASK); +``` + +Valor final: 0x0607 (1543). + +Enums também podem substituir blocos de `#define`: + +```c +enum +{ + TEAM_NONE, + TEAM_COP, + TEAM_ROBBER, + TEAM_CIV, + TEAM_CLERK, + TEAM_DRIVER +} +``` + +Os valores continuam 0–5 e o uso é idêntico. Você pode ainda transformá-los em bitmasks para permitir múltiplos times: + +```c +enum (<<= 1) +{ + TEAM_NONE, + TEAM_COP = 1, + TEAM_ROBBER, + TEAM_CIV, + TEAM_CLERK, + TEAM_DRIVER, + TEAM_ADMIN +} +``` + +Operações básicas: + +- Adicionar: `gPlayerTeam[playerid] |= TEAM_COP` +- Remover: `gPlayerTeam[playerid] &= ~TEAM_COP` +- Verificar: `if (gPlayerTeam[playerid] & TEAM_COP)` + +## `forward` + +Informa ao compilador que uma função será declarada depois. Obrigatório para todas as `public`, mas útil em outros casos, como funções que retornam tags. + +```c +forward MyPublicFunction(playerid, const string[]); + +public MyPublicFunction(playerid, const string[]) +{ +} +``` + +Sem o `forward`, o compilador precisa “reprocessar” quando encontra uma função que retorna, por exemplo, `Float:` antes da definição. Outra alternativa é declarar a função antes do uso, mas o `forward` evita reorganizar o arquivo. + +## `native` + +Nativas são funções implementadas fora do script (no servidor ou em plugins). Você só pode declará-las se já existir uma implementação; porém, pode criar “falsas” nativas para listar funções no painel do Pawno. + +```c +native printf(const format[], {Float,_}:...); +``` + +Para exibir uma função personalizada na lista sem declará-la de fato: + +```c +/* +native MyFunction(playerid); +*/ +``` + +O Pawno não interpreta comentários e adiciona o símbolo na lista, enquanto o compilador ignora a linha. + +Também é possível renomear ou encapsular nativas: + +```c +native my_print(const string[]) = print; + +print(const string[]) +{ + my_print("Someone called print()"); + my_print(string); +} +``` + +Agora toda chamada a `print` passa pelo wrapper. + +## `new` + +Declara variáveis; por padrão começam em 0. + +```c +new + myVar = 5; +``` + +O escopo depende das chaves onde é declarado. Variáveis globais (`new` fora de funções) podem ser usadas em qualquer ponto após a declaração. Se o arquivo incluir outros, eles também terão acesso, a menos que você use `static`. + +## `operator` + +Permite sobrecarregar operadores para tags personalizadas. Exemplo para armazenar números em big endian: + +```c +stock BigEndian:operator=(value) +{ + return BigEndian:(((value >>> 24) & 0x000000FF) | + ((value >>> 8) & 0x0000FF00) | + ((value << 8) & 0x00FF0000) | + ((value << 24) & 0xFF000000)); +} +``` + +Operadores suportados: `+`, `-`, `*`, `/`, `%`, `++`, `--`, `==`, `!=`, `<`, `>`, `<=`, `>=`, `!` e `=`. + +Eles podem executar qualquer lógica, inclusive ignorar o comportamento padrão. + +## `public` + +Transforma funções (ou variáveis) em símbolos acessíveis pelo servidor. Todas as callbacks do SA:MP são `public`. O nome textual da função é armazenado no AMX, o que permite chamá-la por `CallLocalFunction`, `SetTimerEx`, etc. Para isso é obrigatório declarar um `forward`. + +```c +forward MyPublicFunc(); + +main() +{ + CallLocalFunction("MyPublicFunc", ""); +} + +public MyPublicFunc() +{ + printf("Hello"); +} +``` + +Funções públicas também podem ser chamadas normalmente dentro do script (`MyPublicFunc();`), o que é bem mais rápido do que invoques por string. + +## `static` + +`static` aplicado globalmente cria variáveis visíveis apenas no arquivo (ou `#section`) onde foram declaradas. Diferente de `new`, a visibilidade não se estende a arquivos incluídos. + +Localmente, `static` mantém o valor entre chamadas: + +```c +MyFunc() +{ + static + counter = 0; + printf("%d", counter); + counter++; +} +``` + +A primeira chamada imprime 0, a segunda 1 e assim por diante. Com `new`, o valor seria reiniciado em cada execução. Também é possível declarar funções `static` para “privatizá-las”. + +## `stock` + +`stock` serve para declarar funções ou variáveis opcionais, sem gerar avisos de “unused”. Se o símbolo for usado, o compilador o inclui; caso contrário, ele é descartado. + +```c +stock Func1() +{ + printf("Hello"); +} + +stock Func2() +{ + printf("Hi"); +} +``` + +Útil para bibliotecas, onde você não sabe quais funções o usuário vai chamar. Diferentemente de `#pragma unused`, o código nem chega a ser emitido se não houver uso. diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Operators.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Operators.md new file mode 100644 index 00000000000..2ee89fa5ec6 --- /dev/null +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Operators.md @@ -0,0 +1,107 @@ +--- +title: "Palavras-chave: operadores" +sidebar_label: "Palavras-chave: operadores" +--- + +## `char` + +Retorna quantas células (de 4 bytes) são necessárias para armazenar determinado número de caracteres em uma string compactada. + +```c +4 char // 1 +3 char // 1 (não existe ¾ de célula) +256 char // 64 (256 ÷ 4) +``` + +Utilize principalmente em declarações de arrays: + +```c +new + buffer[40 char]; // 10 células +``` + +Consulte o `pawn-lang.pdf` para saber mais sobre strings compactadas. + +## `defined` + +Verifica se um símbolo existe, geralmente dentro de `#if`: + +```c +new + someVar = 5; +#if defined someVar + printf("%d", someVar); +#else + #error The variable 'someVar' isn't defined +#endif +``` + +Uso típico: habilitar trechos de código dependendo de um `#define`. + +```c +#define FILTERSCRIPT + +#if defined FILTERSCRIPT + +public OnFilterScriptInit() +{ + return 1; +} + +#else + +public OnGameModeInit() +{ + return 1; +} + +#endif +``` + +## `sizeof` + +Retorna o tamanho de um array em **elementos**: + +```c +new + someVar[10]; +printf("%d", sizeof (someVar)); // 10 +``` + +Para arrays multidimensionais: + +```c +new + someVar[2][10]; +printf("%d %d", sizeof (someVar), sizeof (someVar[])); // 2 10 +``` + +## `state` + +Relacionado ao sistema de autômatos da linguagem Pawn; não é abordado aqui. + +## `tagof` + +Retorna o identificador numérico da tag de uma variável: + +```c +new + someVar, + Float:someFloat; +printf("%d %d", tagof (someVar), tagof (someFloat)); +``` + +Os valores exibidos correspondem aos códigos internos das tags. Para verificar se uma variável possui determinada tag: + +```c +new Float:fValue = 6.9; + +if (tagof (fValue) == tagof (Float:)) +{ + print("float"); +} +else +{ + print("not a float"); +} +``` diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Style.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Style.md new file mode 100644 index 00000000000..4d2ca98ae48 --- /dev/null +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Style.md @@ -0,0 +1,261 @@ +--- +title: "Guia de estilo Pawn" +sidebar_label: "Guia de estilo Pawn" +description: Um guia breve sobre a convenção de nomes e outros aspectos do código Pawn para facilitar a comunicação de intenção e agilizar depuração e compartilhamento. +--- + +Este documento resume as convenções geralmente aceitas para nomeação e demais aspectos do código-fonte Pawn a fim de facilitar a comunicação de intenções e tornar a depuração e o compartilhamento de código mais simples. + +Veja também: + +- [Modern Pawn](https://github.com/Southclaws/sampctl/wiki/Modern-Pawn) +- [Pawn Package](https://github.com/Southclaws/sampctl/wiki/Packages) + +## Terminologia + +### Statement (instrução) + +Uma instrução é um trecho de código que ordena imperativamente que o programa faça algo. Ela é um pedaço válido de código que produz algum resultado. + +```c +a = b + c; +``` + +Esta é uma instrução composta por uma variável recebendo o resultado de uma [#Expression]. + +```c +SetPlayerColor(playerid, 0xFF4700FF); +``` + +Esta é uma instrução que manda o programa chamar uma função com determinados argumentos. + +```c +x + 8 +``` + +Isto _não_ é uma instrução porque o resultado não é usado em lugar nenhum; é apenas uma [#Expression]. + +### Compound Statement + +Uma instrução composta é um conjunto de instruções delimitadas por chaves. + +```c +{ + new message[] = "hi!"; + print(message); +} +``` + +Este é um bloco composto por duas instruções. + +```c +if (a == b) +{ + print("hello!"); +} +``` + +Este é um bloco composto com uma condição `if`, normalmente chamado de “if statement”. + +```c +return Function1(), Function2(), Function3(); +``` + +Isto _não_ é um bloco composto, e sim uma cadeia de instruções separadas por vírgulas. Esse encadeamento é considerado má prática. + +### Expression (expressão) + +Uma expressão é uma construção sintática que produz um valor; ela não é uma instrução válida a menos que o valor seja usado de alguma forma. + +Expressões normalmente são combinadas para formar instruções. + +```c +a + b +``` + +Esta é uma expressão de soma simples que recebe dois valores e aplica o operador de adição. + +## Diretrizes + +### Chaves + +Prefira o estilo Allman: + +```pawn +function() +{ + // +} +``` + +Porém, se você não consegue evitar, o estilo K&R também é válido em Pawn: + +```pawn +function() { + // +} +``` + +### Switches + +`switch` deve usar dois níveis de indentação, um para o bloco `switch` e outro para cada `case` ou bloco composto. + +```pawn +switch (variable) +{ + case 0: + return 0; + case 1: + return 1; + case 2: + return 2; + default: + return -1; +} +``` + +```pawn +switch (variable) +{ + case 0: + { + // code... + return 0; + } + case 1: + { + // code... + return 1; + } + case 2: + { + // code... + return 2; + } + default: + { + // code... + return -1; + } +} +``` + +### Blocos + +Blocos sempre devem usar chaves, mesmo que contenham apenas uma linha de código. Isso vale para todas as estruturas, inclusive funções. + +```pawn +func() +{ + singleLineExpr(); +} +``` + +```pawn +func() +{ + if () + { + singleLineExpr(); + } +} +``` + +```pawn +func() +{ + if () + { + singleLineExpr(); + } + else if () + { + // + } + else + { + // + } +} +``` + +### Nomeação + +#### Funções + +Funções devem usar `PascalCase`. + +#### Variáveis globais + +Variáveis globais declaradas com `new` devem usar `g_` seguido de `PascalCase` (por exemplo `g_VariableName`). Se forem declaradas com `static`, use `s_` com `PascalCase` (por exemplo `s_VariableName`). + +Globais constantes devem usar `SCREAMING_SNAKE_CASE`. + +#### Variáveis locais + +Variáveis locais devem sempre usar `camelCase` e nunca nomes de uma única letra, exceto: + +- `i`, `j`, `k` etc. em laços `for` +- `x`, `y`, `z` etc. em contextos matemáticos + +#### Enumeradores + +Enumeradores nomeados devem ser prefixados com `E_` (tag forte) ou `e_` (tag fraca). + +Os campos do enumerador também devem usar `SCREAMING_SNAKE_CASE` e o nome do enumerador como prefixo. + +```pawn +static enum E_PLAYER_DATA { + E_PLAYER_CASH, + Float:E_PLAYER_HEALTH, +} +``` + +Usando uma tag fraca: + +```pawn +static enum e_PLAYER_DATA { + E_PLAYER_CASH, + Float:E_PLAYER_HEALTH, +} +``` + +Enumeradores não nomeados também devem usar `SCREAMING_SNAKE_CASE` e o nome do enumerador como prefixo. + +```pawn +static enum { + ENUMATOR_INTEGER, + Float:ENUMATOR_FLOAT, +} +``` + +Enumeradores devem ser declarados como `static`, a menos que sejam usados fora do módulo. + +#### Macros e definições do pré-processador + +Macros devem sempre usar `SCREAMING_SNAKE_CASE`, não importa o uso. + +Definições do pré-processador (constantes) também devem usar `SCREAMING_SNAKE_CASE`. + +Isso ajuda a diferenciar variáveis de constantes e funções de macros. + +De forma geral, evite inventar novas construções sintáticas para não confundir quem está começando sobre o que faz parte da linguagem e o que vem de bibliotecas. + +Algumas bibliotecas antigas fazem isso e não podem ser alteradas por questões de compatibilidade. + +### Documentação + +Documente funções exportadas com um comentário de linha no formato `// FunctionName faz X, Y e Z e retorna A`, em que a primeira palavra é o nome da função seguido de uma breve descrição. Não há necessidade de citar cada parâmetro. Exemplo: + +```pawn +// LoadPlayerAccount é chamada para iniciar o processo de carregamento da conta. +// Esta função dispara chamadas HTTP para obter dados do jogador, exibe diálogos +// e, quando o processo termina com sucesso, o evento `OnPlayerLogin` é emitido. +// Em caso de falha, o jogador é desconectado. +stock Error:LoadPlayerAccount(playerid) +{ + // code... +} +``` + +Cada pacote deve ter um `README` e, se necessário, cada módulo deve ter um comentário logo na primeira linha descrevendo o que ele oferece. diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Tags.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Tags.md new file mode 100644 index 00000000000..3f7a658f5df --- /dev/null +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Tags.md @@ -0,0 +1,181 @@ +--- +title: "Scripting: Tags" +sidebar_label: "Scripting: Tags" +description: Um guia sobre Tags, o recurso semelhante a tipos da linguagem Pawn que oferece segurança ao trabalhar com valores de diferentes propósitos. +--- + +## Introdução + +Uma tag é um prefixo aplicado a uma variável para indicar ao compilador que ela deve ser tratada de forma especial em determinadas situações. Por exemplo, você pode usar tags para determinar onde uma variável pode ou não ser usada, ou ainda definir uma forma específica de somar duas variáveis. + +Existem dois tipos de tags: tags fortes (usam letra maiúscula) e tags fracas (usam letra minúscula). Em geral elas funcionam igual, mas em algumas situações as tags fracas podem ser convertidas silenciosamente para “sem tag” pelo compilador (ou seja, você não verá um aviso). Na maioria das vezes, com tags fracas, e sempre com tags fortes, mudar a tag implicitamente gera um aviso indicando que os dados podem estar sendo usados de forma incorreta. + +Um exemplo simples: + +```c +new + File:myfile = fopen("file.txt", io_read); +myFile += 4; +``` + +A função `fopen` retorna um valor com tag `File:`; não há problema porque esse valor é armazenado em uma variável também marcada com `File:` (observe que as letras maiúsculas/minúsculas são idênticas). Contudo, na linha seguinte o valor `4` é somado ao handle do arquivo. O `4` não possui tag (na verdade tem o tipo `_:` por padrão, mas não é algo com que você precise se preocupar normalmente) e `myFile` tem a tag `File:`. Como nada e algo não podem ser iguais, o compilador emitirá um aviso; isso é bom, pois um handle de arquivo não faz sentido em termos de valor numérico, então modificá-lo apenas corromperia o handle e impediria o fechamento do arquivo, já que não haveria mais um handle válido para passar ao fechamento. + +### Tags fortes + +Como dito, uma tag forte começa com letra maiúscula. Exemplos em SA:MP: + +```c +Float: +File: +Text: +``` + +Elas não podem ser misturadas com outros tipos e sempre geram aviso quando você tenta fazer isso: + +```c +new + Float:myFloat, + File:myFile, + myBlank; + +myFile = fopen("file.txt", io_read); // File: = File:, sem aviso + +myFloat = myFile; // Float: = File:, aviso "tag mismatch" + +myFloat = 4; // Float: = _: (nenhuma), aviso "tag mismatch" + +myBlank = myFloat; // _: (nenhuma) = Float:, aviso "tag mismatch" +``` + +### Tags fracas + +Uma tag fraca se comporta quase como uma tag forte, mas o compilador não gera aviso quando o destino não tem tag e a origem é uma tag fraca. Compare o código com tag forte e com tag fraca: o primeiro gera aviso, o segundo não. + +```c +new + Strong:myStrong, + weak:myWeak, + myNone; + +myNone = myStrong; // Aviso +myNone = myWeak; // Sem aviso +``` + +O inverso não é verdadeiro: + +```c +myWeak = myNone; // Aviso +``` + +Isso também vale para funções: chamar uma função cujo parâmetro é sem tag passando uma variável com tag fraca não gera aviso: + +```c +new + weak:myWeak; +MyFunction(myWeak); + + + +MyFunction(myVar) +{ + ... +} +``` + +Mas chamar uma função que espera um parâmetro etiquetado (fraco ou forte) com um valor sem tag gera aviso. Exemplos de tags fracas usados em SA:MP, ainda que pouco conhecidos, incluem: + +```c +bool: +filemode: +floatround_method: +``` + +## Uso + +### Declaração + +Declarar uma variável com tag é simples: basta escrever a tag; não é necessário defini-la antes, embora isso seja possível e útil em alguns casos (veremos mais à frente): + +```c +new + Mytag:myVariable; +``` + +Declarar com uma tag existente permite usar essa variável com as funções e operadores já definidos para o tipo. + +### Funções + +Criar uma função que recebe ou retorna uma tag também é simples: prefixe a parte relevante com o tipo desejado, por exemplo: + +```c +Float:GetValue(File:fHnd, const name[]) +{ + ... +} +``` + +Essa função recebe o handle de um arquivo e retorna um float (presumivelmente um valor lido desse arquivo e correspondente ao nome informado em `name[]`). Ela provavelmente usará `floatstr`, que também retorna um `Float:` (é possível conferir na barra de status do Pawno ao clicar na função). A implementação em si não importa, mas ela converte a string em um float IEEE, armazenado como um cell (na prática é um inteiro com o mesmo padrão de bits do número IEEE, já que Pawn não tem tipagem; tags existem justamente para amenizar isso). + +### Operadores + +Operadores como `+`, `==`, `>` etc. podem ser sobrecarregados para tags diferentes, ou seja, fazer `+` em dois `Float:` executa algo distinto de somar duas variáveis sem tag. Isso é especialmente útil para floats porque, como mencionado, eles são apenas inteiros com um padrão de bits específico; se os operadores não fossem sobrecarregados, as operações ocorreriam sobre os inteiros e o resultado, interpretado como float, seria lixo. Por isso a tag `Float:` tem versões sobrecarregadas da maioria dos operadores para chamar funções especiais no servidor em vez de confiar em Pawn. + +Um operador é igual a uma função normal, mas com o nome `operator(**símbolo**)`, onde (**símbolo**) é o operador que você quer sobrescrever. Os operadores válidos são: + +```c ++ +- += +++ +-- +== +* +/ +!= +> +< +>= +<= +! +% +``` + +Operadores como `\`, `*`, `=` etc. já são tratados automaticamente. Operadores como `&` não podem ser sobrecarregados. Você também pode sobrescrever o mesmo operador várias vezes com combinações diferentes de tags. Exemplo: + +```c +stock Float:operator=(Mytag:oper) +{ + return float(_:oper); +} +``` + +Se adicionar isso ao código e fizer: + +```c +new + Float:myFloat, + Mytag:myTag; + +myFloat = myTag; +``` + +Você não verá mais o aviso do compilador porque agora o operador `=` para o caso `Float: = Mytag:` está implementado e o compilador sabe como tratar a conversão. + +### Sobrescrita + +No exemplo de sobrecarga acima, a linha funcional foi: + +```c +return float(_:oper); +``` + +Esse é um exemplo de sobrescrita de tag: o `_:` antes de `oper` indica ao compilador que ignore o fato de a variável ter tag `Mytag:` e a trate como `_:` (sem tag). A função `float()` aplica a tag a um número comum, então precisa receber um número sem tag. No exemplo consideramos que `Mytag` armazena um inteiro comum, mas é necessário cuidado com a sobrescrita; o código a seguir gera resultados estranhos: + +```c +new + Float:f1, + Float:f2 = 4.0; +f1 = float(_:f2); +``` + +Seria lógico imaginar que `f1` viraria `4.0`, mas isso não acontece. Como mencionado, `f2` guarda a representação de `4.0`, e não apenas o valor inteiro `4`; isso significa que o valor bruto contido na variável é um número bem incomum. Ao mandar o compilador tratá-la como inteiro, ele simplesmente interpreta o padrão de bits como um inteiro, sem converter o float, então o resultado será praticamente aleatório (há um padrão nos floats IEEE, mas certamente não se parecerá com `4.0`). diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Variables.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Variables.md new file mode 100644 index 00000000000..8fddf8a92e8 --- /dev/null +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/Variables.md @@ -0,0 +1,218 @@ +--- +title: "Noções básicas: Variáveis" +sidebar_label: "Noções básicas: Variáveis" +description: Um guia introdutório sobre variáveis em Pawn +--- + +## Variáveis + +Um dos conceitos mais importantes em programação é o de “variáveis”. Em programação, uma variável é uma entidade mutável, mas em que sentido? Em Pawn, uma variável guarda um “valor” a qualquer momento e esse valor — como o próprio nome sugere — é “variável” ou “mutável”. + +Variáveis são importantes porque são unidades pequenas de memória que podem guardar ou “lembrar” valores diferentes enquanto o programa está em execução, e essa propriedade é extremamente útil. Por exemplo, se você quiser acompanhar a pontuação de 100 jogadores em um jogo, basta programar o computador para armazenar (lembrar) e atualizar esses valores. Depois, se quiser calcular a média dessas pontuações ou montar um placar, os valores guardados nas variáveis podem ser acessados facilmente e usados para esse fim. + +### Declarando variáveis + +O seguinte é o formato básico de declaração: + +```c +// Criando (mais precisamente, declarando) uma variável chamada 'myVariable' + +new myVariable; + +// A palavra-chave 'new' é usada para declarar uma variável +// Na linha acima, declaramos uma variável com o nome 'myVariable' +// O ponto e vírgula fecha a instrução de declaração. +``` + +O formato fica mais claro com alguns exemplos: + +```c +new var; +new ammo; +new score; +new vehicles; +new topScore; +``` + +Cada variável acima tem o valor zero por padrão. Há diferentes maneiras de atribuir valores a uma variável; uma delas é definir o valor no momento da declaração: + +```c +new letters = 25; +``` + +No exemplo acima, declaramos a variável `letters` com o valor 25. Note o sinal de igualdade, que é um operador de atribuição simples usado para definir valores. Ele avalia a expressão do lado direito e envia o resultado para a variável referenciada do lado esquerdo. Além de atribuir valores diretamente na declaração, você pode fazer isso em outra parte do código: + +```c +new letters; + +letters = 25; +``` + +### Escopos + +Só é possível modificar o valor de uma variável se a parte do código onde ela é usada estiver dentro do escopo dessa variável. O escopo depende do bloco ou da posição em que ela foi declarada. Por exemplo, uma variável declarada fora de qualquer bloco — geralmente no início do script — tem escopo global e pode ser acessada de qualquer parte do script: + +```c +#include + +new g_var = 5; + +public OnFilterScriptInit() +{ + g_var = 10; + + printf("The value is %i", g_var); + + return 1; +} + +public OnPlayerConnect(playerid) +{ + g_var = 100; + + printf("The value is %i", g_var); + + return 1; +} + +// Saída : +// The value is 10 +// The value is 100 + +// Observação: a segunda linha aparece apenas quando um jogador se conecta. +``` + +Além das variáveis globais, existem variáveis “locais” ou “privadas” que só podem ser acessadas dentro do bloco onde foram declaradas. + +```c +#include + +public OnFilterScriptInit() +{ + new localVar; + + localVar = 5; + + return 1; +} + +public OnPlayerConnect(playerid) +{ + localVar = 10; // Esta linha causará um erro na compilação + + return 1; +} +``` + +Se você compilar o código acima, o compilador exibirá um erro porque a variável local está sendo referenciada em outro bloco totalmente diferente. Observação: se for um bloco interno (aninhado), a variável pode ser acessada a partir dele. + +Outro ponto importante é que não é possível declarar variáveis com o mesmo nome se seus escopos se interceptam. Por exemplo, se já existir uma variável `score` em escopo global, você não pode criar outra `score` também global nem uma local com esse nome, e o inverso também vale (se já existir uma variável local, evite declarar uma global com o mesmo nome). + +```c +#include + +new g_score; + +public OnFilterScriptInit() +{ + new g_score = 5; // Esta linha causará um erro. + return 1; +} +``` + +### Regras de nomenclatura + +Agora que você sabe declarar variáveis, precisa conhecer as regras de nomes: + +- Todo nome deve começar com uma letra ou sublinhado (`_`). +- Depois do primeiro caractere, nomes podem conter letras e números, mas não espaços nem caracteres especiais. +- Os nomes diferenciam maiúsculas de minúsculas. +- Usar uma palavra reservada (keyword) como nome gera erro. + +#### Exemplos: + +```c +new new; // Incorreto: palavra reservada +new _new; // Correto + +new 10letters; // Incorreto: começa com número +new letters10; // Correto +new letters_10; // Correto + +new my name; // Incorreto: espaço no nome +new my_name; // Correto + +new !nternet; // Incorreto +new Internet; // Correto +``` + +### Armazenando diferentes tipos de dados + +Agora vejamos exemplos dos tipos de dados que podem ser armazenados e como fazer isso: + +```c +new letter = 'M'; + + +new value = 100; + + +new decimalValue = 1.0; +// Funciona, mas mostrará um aviso do compilador +// warning 213: tag mismatch + + +new engineOn = true; +// Funciona sem aviso, mas é recomendado usar uma Tag + + +new sentence = "This is a sentence"; +// Mostrará um erro. +// error 006: must be assigned to an array +``` + +Uma variável pode guardar um caractere, um inteiro, um booleano (true ou false) e um valor decimal (float). Os comentários acima mostram que tentar armazenar uma string em uma variável comum resulta em erro (strings só podem ser armazenadas em _arrays_). Além disso, atribuir um valor float sem tag gera um aviso do compilador, que pode ser evitado usando “tags”. Sem as tags corretas o script ainda compila, mas o aviso aparece. As tags dizem ao compilador qual tipo de dado deve ser guardado e, assim, ele consegue nos alertar com avisos ou erros quando fazemos algo que pode quebrar o programa. Exemplos: + +```c +new decimalValue = 1.0; // Incorreto +new bool:decimalValue = 1.0 // Incorreto +new Float:decimalValue = 1.0; // Correto + +new switchOn = 1.0; // Incorreto +new switchOn = true; // Incorreto, não mostra aviso +new bool:switchOn = true; // Correto +``` + +Usar as tags corretas é essencial para evitar bugs ou erros durante a execução. + +Como Pawn é uma linguagem sem tipos fixos, ele permite guardar tipos de dados diferentes na mesma variável. Isso pode ser útil em alguns casos e problemático em outros, portanto não é recomendado. + +```c +#include + +public OnFilterScriptInit() +{ + + new var; + + var = 'a'; + printf("%c", var); + + var = 1; + printf("%d", var); + + var = 1.0; + printf("%f", var); + + var = true; + printf("%d", var); // imprime 0 ou 1 + + return 1; +} + +// Saída : +a +1 +1.000000 +1 +``` diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/reference/00-Contents.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/reference/00-Contents.md new file mode 100644 index 00000000000..055eeea8d7e --- /dev/null +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/reference/00-Contents.md @@ -0,0 +1,78 @@ +# PAWN + +![Pawn](https://i.ibb.co/SRbRt3C/pawn-icon-daddy-DOT-small.png) + +### linguagem de script embarcada + +##### Fevereiro de 2006 + +--- + +##### ITB CompuPhase + +##### ii + +--- + +“Java” é marca registrada da Sun Microsystems, Inc. + +“Microsoft” e “Microsoft Windows” são marcas registradas da Microsoft Corporation. + +“Linux” é marca registrada de Linus Torvalds. + +“CompuPhase” é marca registrada da ITB CompuPhase. + +“Unicode” é marca registrada da Unicode, Inc. + +Copyright © 1997–2006, ITB CompuPhase + +Eerste Industriestraat 19–21, 1401VL Bussum, Países Baixos +telefone: (+31)-(0)35 6939 261 + +e-mail: info@compuphase.com, http://www.compuphase.com + +As informações deste manual e o software associado são fornecidos “como estão”. Não há garantias, explícitas ou implícitas, sobre a exatidão do software e da documentação. + +Pedidos de correções e complementos ao manual ou ao software podem ser enviados para o endereço acima. + +Diagramado com TeX usando as fontes “Computer Modern” e “Palatino”, tamanho base de 11 pontos. + +--- + +# Sumário + +--- + +[Prefácio](01-Foreword) — páginas 3–5 + +[Introdução guiada](02-A-tutorial-introduction) — páginas 5–62 + +[Dados e declarações](03-Data-and-declarations) — páginas 62–70 + +[Funções](04-Functions) — páginas 70–93 + +[O pré-processador](05-The-preprocessor) — páginas 93–97 + +[Sintaxe geral](06-General-syntax) — páginas 97–104 + +[Operadores e expressões](07-Operators-and-expressions) — páginas 104–112 + +[Instruções](08-Statements) — páginas 112–117 + +[Diretivas](09-Directives) — páginas 117–124 + +[Biblioteca de funções proposta](10-Proposed-function-library) — páginas 124–134 + +[Armadilhas: diferenças em relação ao C](11-Pitfalls-differences-from-C) — páginas 134–137 + +[Dicas variadas](12-Assorted-tips) — páginas 137–148 + +[Apêndices](13-Appendices) — páginas 148–183 + +∟ [Mensagens de erro e aviso](13-Appendices#error-and-warning-messages) — páginas 148–168 + +∟ [O compilador](13-Appendices#the-compiler) — páginas 168–174 + +∟ [Justificativa](13-Appendices#rationale) — páginas 174–181 + +∟ [Licença](13-Appendices#license) — páginas 181–183 diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/reference/01-Foreword.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/reference/01-Foreword.md new file mode 100644 index 00000000000..30109b316b9 --- /dev/null +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/reference/01-Foreword.md @@ -0,0 +1,29 @@ +# Prefácio + +--- + +“pawn” é uma linguagem de script simples, sem tipos e de 32 bits, com sintaxe parecida com C. Velocidade de execução, estabilidade, simplicidade e baixo consumo de recursos foram critérios essenciais tanto para a linguagem quanto para o interpretador/máquina abstrata que executa um programa pawn. + +Nenhuma aplicação ou ferramenta consegue fazer tudo para todos os usuários. Isso não apenas impulsiona novos sistemas, como explica a presença de opções extensas de configuração e linguagens de macro/script dentro de softwares. Nas minhas próprias aplicações sempre existiram “mini linguagens”: algumas extremamente simples, outras bem abrangentes... e, na maioria dos casos, as necessidades poderiam ser atendidas por uma linguagem de propósito geral acompanhada de uma biblioteca específica. Assim surgiu o pawn. + +A linguagem pawn foi desenhada para manipular objetos dentro de uma aplicação hospedeira. O conjunto de ferramentas (compilador e máquina abstrata) foi escrito para ser facilmente extensível e capaz de rodar em diferentes arquiteturas de software e hardware. + +## ♦ + +pawn descende do Small C original, de Ron Cain e James Hendrix, que por sua vez era um subconjunto de C. Algumas das modificações que fiz no Small C —como remover o sistema de tipos e substituir ponteiros por referências— foram tão profundas que já não era apropriado chamar minha linguagem de “subconjunto de C” ou “dialeto de C”. Por isso tirei o “C” do título e passei a publicar sobre a linguagem com o nome “Small” na revista Dr. Dobb’s Journal e nos anos seguintes. Durante o desenvolvimento e manutenção do produto recebi inúmeros pedidos de mudança; um dos mais frequentes era adotar outro nome, pois procurar por “small scripting language” na Internet era dificultado pelo termo “small” ser comum demais. A mudança de nome coincidiu com outra alteração importante da linguagem: o suporte a “estados” (e máquinas de estados). + +Sou grato a Ron Cain e James Hendrix (e, mais recentemente, Andy Yuen) e à revista Dr. Dobb’s Journal por terem colocado esse projeto em movimento. Embora eu tenha revisado praticamente todas as linhas do código original algumas vezes, as origens em Small C ainda são visíveis. + +## ♦ + +--- + +Um panorama detalhado dos objetivos de design e dos compromissos assumidos está no apêndice C; aqui faço um resumo de alguns pontos-chave. Como dito anteriormente, pawn serve para customizar aplicações (escrevendo scripts), não para escrever aplicações completas. Ele é modesto em termos de estrutura de dados porque os programas pawn existem para manipular objetos (texto, sprites, fluxos, consultas etc.) dentro da aplicação hospedeira, e esse programa, por design, não tem acesso direto a dados fora da sua máquina abstrata. A única forma de interagir com objetos na aplicação host é chamando sub-rotinas —as “funções nativas” expostas por ela. + +Pawn é flexível justamente nessa área crucial: a chamada de funções. Ele suporta valores padrão para **qualquer** argumento (não apenas o último), chamada por referência e por valor, e argumentos nomeados ou posicionais. Como é uma linguagem sem tipos, não possui verificação de tipos tradicional; em vez disso, oferece um mecanismo de “classificação” chamado “tags”. Esse sistema é especialmente útil em parâmetros de funções, pois cada argumento pode aceitar múltiplas tags. + +Creio que o ponto forte do pawn não está em um recurso isolado, e sim na combinação deles. Argumentos nomeados permitem especificar parâmetros em qualquer ordem; valores padrão possibilitam omitir os que não importam para o contexto. Juntos, criam uma forma prática e “descritiva” de chamar (funções nativas) para manipular objetos na aplicação hospedeira. + +--- + +[Voltar ao sumário](00-Contents) diff --git a/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/reference/02-A-tutorial-introduction.md b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/reference/02-A-tutorial-introduction.md new file mode 100644 index 00000000000..2917edaba9d --- /dev/null +++ b/frontend/i18n/pt-BR/docusaurus-plugin-content-docs/current/scripting/language/reference/02-A-tutorial-introduction.md @@ -0,0 +1,2265 @@ +# Introdução guiada + +--- + +Pawn é uma linguagem de programação simples cuja sintaxe lembra a da linguagem C. Um programa pawn é formado por um conjunto de funções e um conjunto de variáveis. As variáveis representam objetos de dados e as funções agrupam instruções (chamadas de _statements_) que operam sobre esses dados ou executam tarefas específicas. + +O primeiro programa demonstrado em praticamente qualquer linguagem costuma imprimir uma mensagem simples; o clássico é “Hello world”. Em pawn o programa fica assim: + +Listagem: hello.p + +```c + +main() + printf "Hello world\n" + +``` + +Este manual parte do princípio de que você já sabe executar um programa pawn; caso contrário, consulte o manual da aplicação (há mais dicas na página 168). + +A execução de um programa pawn começa em uma função de “entrada”∗ —neste manual, quase todos os exemplos usam a função `main` para esse papel. No exemplo acima, `main` contém apenas uma instrução, logo abaixo do cabeçalho da função. Quebras de linha e indentação não são significativas; poderíamos invocar `printf` na mesma linha do cabeçalho de `main`. + +A definição de uma função exige que o nome seja seguido por um par de parênteses. Se a função receber parâmetros, eles são declarados entre os parênteses. `main` não recebe nenhum parâmetro. Já as regras mudam quando chamamos uma função: os parênteses são opcionais na chamada de `printf`. + +O único argumento passado a `printf` é uma string, delimitada por aspas duplas. Os caracteres `\n` perto do final formam uma sequência de escape —nesse caso, representam a quebra de linha. Ao encontrar a sequência `\n`, `printf` move o cursor para a primeira coluna da próxima linha. +the next line. One has to use the \n escape sequence to insert a “newline” into +the string, because a string may not wrap over multiple lines. + +--- + +###### ∗ This should not be confused with the “state” entry functions, which are called entry, but serve a different purpose — see page 42. + +`Compiling and running scripts: see page 168` + +`String literals: 99 Escape sequence: 99` + +--- + +pawn is a “case sensitive” language: upper and lower case letters are considered +to be different letters. It would be an error to spell the function printf in the +acima como “PrintF”. Palavras-chave e símbolos pré-definidos, como o nome +da função `main`, devem ser escritos em minúsculas. + +Se você conhece C, talvez ache que o exemplo anterior não se parece muito +com o tradicional “Hello world” de C/C++. No entanto, pawn também pode +lembrar bastante C. O próximo exemplo é sintaxe válida em pawn (e tem a +mesma lógica do anterior): + +Listing: hello.p — C style + +```c + +#include + +main() +{ + printf("Hello world\n"); +} + +``` + +Esses primeiros exemplos já revelam algumas diferenças entre pawn e C: + +- normalmente não é necessário incluir arquivos de cabeçalho fornecidos pelo sistema; +- pontos e vírgulas são opcionais (exceto ao escrever várias instruções na mesma linha); +- quando o corpo de uma função tem apenas uma instrução, as chaves (que transformam em instrução composta) também são opcionais; +- se você não usa o resultado de uma função em uma expressão ou atribuição, os parênteses ao redor dos argumentos são opcionais. + +Esses itens referem-se a sintaxes opcionais —use o estilo que preferir; nenhum deles é “desaconselhado” ou “nocivo”. Os exemplos deste manual posicionam as chaves e usam uma indentação conhecida como “estilo Whitesmith”, mas pawn é uma linguagem de formato livre e qualquer estilo de recuo funciona. + +Como pawn foi projetado como linguagem de extensão de aplicações, o conjunto de funções disponível em um programa depende da aplicação hospedeira. Consequentemente, a linguagem não conhece nenhuma função intrinsecamente. A função `print`, usada nos primeiros exemplos, precisa ser fornecida pelo aplicativo host e “declarada” ao analisador de pawn. + +--- + +###### ∗ In the language specification, the term “parser” refers to any implementation that processes and runs on conforming Pawn programs —either interpreters or compilers. + +`More function descriptions at page 124` + +--- + +Pressupõe-se, porém, que toda aplicação hospedeira disponibilize um conjunto mínimo de funções comuns, como `print` e `printf`. + +Em alguns ambientes é necessário “habilitar” o display ou terminal antes de enviar texto. Se for o caso, chame a função `console` antes da primeira chamada a `print` ou `printf`. A função `console` também permite configurar características do dispositivo, como quantidade de linhas e colunas. Os programas deste manual não a utilizam porque muitas plataformas não exigem nem oferecem esse recurso. + +### • Arithmetic + +Os elementos fundamentais de grande parte dos programas são cálculos, decisões (execuções condicionais), iterações (loops) e variáveis para armazenar dados de entrada, saída e resultados intermediários. O próximo exemplo ilustra vários desses conceitos: ele calcula o máximo divisor comum de dois valores usando o algoritmo de Euclides. + +Listing: gcd.p + +```c + +/* the greatest common divisor of two values, using Euclides’ +algorithm */ + +main() +{ + print "Input two values\n" + new a = getvalue() + new b = getvalue() + while (a != b) + if (a > b) + a = a - b + else + b = b - a + printf "The greatest common divisor is %d\n", a +} + +``` + +Agora `main` contém mais do que uma única instrução `print`. Quando o corpo de uma função possui várias instruções, elas devem ficar dentro de chaves (`{` e `}`), formando uma instrução composta. O mesmo vale para os blocos de `if/else` e para os corpos de loops. + +A palavra-chave `new` cria uma variável; seu nome vem logo em seguida. É comum (mas não obrigatório) atribuir um valor inicial ao declarar a variável. + +`Compound statement: 112` + +`Data declarations are covered in detail starting at page 62` + +--- + +Variáveis devem ser declaradas antes de serem usadas em qualquer expressão. A função `getvalue` (também comum entre as funções pré-definidas) lê um valor do teclado e o retorna. Lembre-se de que pawn é uma linguagem sem tipos: toda variável é uma célula numérica capaz de armazenar um inteiro com sinal. + +O nome `getvalue` é seguido por parênteses porque o valor retornado é armazenado em uma variável. Normalmente os argumentos da função apareceriam entre os parênteses, mas `getvalue` (neste programa) não recebe parâmetros explícitos. Se você não atribuir o resultado de uma função a uma variável nem o usar em outra expressão, os parênteses são opcionais —como acontece com `print` e `printf`. Você pode mantê-los se preferir, mas não é necessário. + +Loop instructions, like “while”, repeat a single instruction as long as the loop +condition (the expression between parentheses) is “true”. One can +execute multiple instructions in a loop by grouping them in a compound statement. +The if–else instruction has one instruction for the “true” clause and one for the “false”. + +Observe that some statements, like while and if–else, contain (or +“fold around”) another instruction —in the case of if–else even two other instructions. +The complete bundle is, again, a single instruction. That is: + +- the assignment statements “a = a - b” below the if and “b = b - a” below the else are statements; + +- the if–else statement folds around these two assignment statements and forms a single statement of itself; + +- the while statement folds around the if–else statement and forms, again, a single statement. + +It is common to make the nesting of the statements explicit by indenting any +sub-statements below a statement in the source text. In the “Greatest Com- +mon Divisor” example, the left margin indent increases by four space characters +after the while statement, and again after the if and else keywords. State- +ments that belong to the same level, such as both printf invocations and the +while loop, have the same indentation. + +The loop condition for the while loop is “(a != b)”; the symbol != is the +“not equal to” operator. That is, the if–else instruction is repeated +until “a” equals “b”. It is good practice to indent the instructions that run under +control of another statement, as is done in the preceding example. + +--- + +The call to printf, near the bottom of the example, differs from the print call +right below the opening brace (“\{”). The “f” in printf stands for “formatted”, +which means that the function can format and print numeric values and other +data (in a user-specified format), as well as literal text. The %d symbol in the +string is a token that indicates the position and the format that the subsequent +argument to function printf should be printed. At run time, the token %d is +replaced by the value of variable “a” (the second argument of printf). + +A função `print` imprime apenas texto e é mais rápida do que `printf`. Se quiser exibir um caractere “%” literal, use `print` ou duplique o símbolo ao usar `printf`. Ou seja: + +`print "20% of the personnel accounts for 80% of the costs\n"` + +e + +`printf "20%% of the personnel accounts for 80%% of the costs\n"` + +produzem a mesma string. + +--- + +`"while" loop: 116` + +`"if else": 114` + +`Relational operators: 107` + +--- + +### • Arrays e constantes + +Além das variáveis simples de uma célula, pawn oferece “variáveis array” que armazenam diversos valores. O exemplo a seguir lista números primos usando o famoso “crivo de Eratóstenes”. Ele também apresenta outro conceito: constantes simbólicas, que se parecem com variáveis, mas não podem ser alteradas. + +Listing: sieve.p + +```c + +/* Print all primes below 100, using the "Sieve of Eratosthenes" */ + +main() +{ + const max_primes = 100 + new series[max_primes] = { true, ... } + for (new i = 2; i < max_primes; ++i) + if (series[i]) + { + printf "%d ", i + /* filter all multiples of this "prime" from the list */ + for (new j = 2 * i; j < max_primes; j += i) + series[j] = false + } +} + +``` + +--- + +Sempre que um programa (ou subprograma) tiver limites fixos, é boa prática criar constantes simbólicas para eles. No exemplo, `max_primes` vale 100 e é usada três vezes após a definição: na declaração do array `series` e nas duas estruturas `for`. Se quisermos adaptar o código para listar primos menores que 500, basta alterar uma linha. + +Assim como variáveis simples, arrays podem ser inicializados na criação. Pawn fornece um atalho para inicializar todos os elementos com o mesmo valor: os cem elementos de `series` recebem `true` —sem que seja necessário digitar a palavra cem vezes. Os símbolos `true` e `false` são constantes pré-definidas. + +Quando uma variável simples (como `i` e `j` na implementação do crivo) é declarada na primeira expressão de um laço `for`, ela existe apenas dentro do loop. Declaração de variável segue suas próprias regras; apesar da aparência, não é uma instrução comum. Uma dessas regras permite declarar uma variável na primeira expressão do `for`. + +Os dois `for` também apresentam novos operadores na terceira expressão: `++` incrementa seu operando em um; ou seja, `++i` equivale a `i = i + 1`. Já `+=` soma a expressão da direita ao valor da esquerda; `j += i` equivale a `j = j + i`. + +Trabalhando com arrays é preciso ficar atento ao problema clássico de “um a mais”: o primeiro elemento de `series` é `series[0]`, logo, se o array tem `max_primes` elementos, o último índice é `series[max_primes - 1]`. Se `max_primes` vale 100, o último elemento é `series[99]`; acessar `series[100]` é inválido. + +`Constant declaration: 101` + +`Progressive initiallers: 65` + +`"for" loop: 113` + +`An overview of all operators: 104` + +### • Funções + +Programas maiores costumam separar tarefas e operações em funções. Isso aumenta a modularidade e, quando bem escritas, elas podem ser reaproveitadas em outros projetos. O exemplo a seguir implementa uma função para calcular a sequência de Fibonacci. + +Essa série foi descrita por Leonardo “Fibonacci”, de Pisa, matemático italiano do século XIII famoso por popularizar os algarismos indo-arábicos no Ocidente. A sequência tinha como objetivo modelar o crescimento de uma população de coelhos idealizados, e segue 1, 1, 2, 3, 5, 8, 13, 21... —cada termo é a soma dos dois anteriores. + +Listing: fib.p + +```c + +/* Calculation of Fibonacci numbers by iteration */ + +main() +{ + print "Enter a value: " + new v = getvalue() + if (v > 0) + printf "The value of Fibonacci number %d is %d\n", v, fibonacci(v) + else + printf "The Fibonacci number %d does not exist\n", v +} +fibonacci(n) +{ + assert n > 0 + new a = 0, b = 1 + (new i = 2; i < n; i++) + { + new c = a + b + a = b + b = c + } + return a + b +} + +``` + +A instrução `assert` no início de `fibonacci` merece destaque: ela protege contra condições “impossíveis” ou inválidas. Números de Fibonacci negativos são inválidos, e `assert` sinaliza isso como erro de programação caso ocorra. Use `assert` apenas para erros do programador, nunca para erros de entrada do usuário. + +Implementar uma função definida pelo usuário se parece bastante com a função `main`. Em `fibonacci` vemos dois conceitos novos: ela recebe um valor por parâmetro e retorna um resultado. + +Os parâmetros são declarados no cabeçalho da função; aqui, há apenas `n`. Dentro da função, o parâmetro se comporta como uma variável local cujo valor foi passado do lado externo no momento da chamada. + +A instrução `return` encerra a função e estabelece o valor retornado. Ela não precisa aparecer apenas ao final; saídas antecipadas são permitidas. + +`"assert" statement: 112` + +`Funcitons: properties & features: 70` + +--- + +The main function of the Fibonacci example calls predefined “native” functions, +like getvalue and printf, as well as the user-defined function fibonacci. +From the perspective of calling a function (as in function main), there is no +difference between user-defined and native functions. + +The Fibonacci numbers sequence describes a surprising variety of natural phe- +nomena. For example, the two or three sets of spirals in pineapples, pine cones +and sunflowers usually have consecutive Fibonacci numbers between 5 and 89 +as their number of spirals. The numbers that occur naturally in branching +patterns (e.g. that of plants) are indeed Fibonacci numbers. Finally, although +the Fibonacci sequence is not a geometric sequence, the further the sequence +is extended, the more closely the ratio between successive terms approaches +the Golden Ratio, of 1.618. . . ∗ that appears so often in art and architecture. + +### • Call-by-reference & call-by-value + +Dates are a particularly rich source of algorithms and conversion routines, +because the calenders that a date refers to have known such a diversity, +through time and around the world. + +The “Julian Day Number” is attributed to Josephus Scaliger† and it counts +the number of days since November 24, 4714 BC (proleptic Gregorian cal- +endar‡). Scaliger chose that date because it marked the coincidence of three +well-established cycles: the 28-year Solar Cycle (of the old Julian calendar), +the 19-year Metonic Cycle and the 15-year Indiction Cycle (periodic taxes or gov- +ernmental requisitions in ancient Rome), and because no literature or recorded +history was known to predate that particular date in the remote past. Scaliger +used this concept to reconcile dates in historic documents, later astronomers +embraced it to calculate intervals between two events more easily. + +--- + +###### ∗ The exact value for the Golden Ratio is 1/2(√5 + 1). The relation between Fibonacci numbers and the Golden Ratio also allows for a “direct” calculation of any sequence number, instead of the iterative method described here. + +###### ∗ There is some debate on exactly what Josephus Scaliger invented and who or what he called it after. + +###### ∗ The Gregorian calendar was decreed to start on 15 October 1582 by pope Gregory XIII, which means that earlier dates do not really exist in the Gregorian calendar. When extending the Gregorian calendar to days before 15 October 1582, we refer to it as the proleptic Gregorian calendar. + +`Native function interface: 85` + +--- + +Julian Day numbers (sometimes denoted with unit “jd”) should not be con- +Aqui estamos falando de “Julian Day Numbers” (JDN), que não devem ser confundidos com “Julian Dates” (número de dias desde o início do ano) nem com o calendário juliano criado por Júlio César. + +O programa abaixo calcula o número de dias julianos a partir de uma data do calendário gregoriano (próleptico) e também faz o caminho inverso. Nesse calendário, o primeiro ano é 1 d.C. e o anterior é 1 a.C.; o ano zero **não** existe. Por isso, o programa considera anos negativos para datas a.C. e positivos (não zero) para anos d.C. + +Listing: julian.p + +```c + +/* calculate Julian Day number from a date, and vice versa */ + +main() +{ + new d, m, y, jdn + print "Give a date (dd-mm-yyyy): " + + d = getvalue(_, ’-’, ’/’) + m = getvalue(_, ’-’, ’/’) + y = getvalue() + jdn = DateToJulian(d, m, y) + + printf("Date %d/%d/%d = %d JD\n", d, m, y, jdn) + print "Give a Julian Day Number: " + + jdn = getvalue() + JulianToDate jdn, d, m, y + + printf "%d JD = %d/%d/%d\n", jdn, d, m, y +} + +DateToJulian(day, month, year) +{ + /* The first year is 1. Year 0 does not exist: it is 1 BC (or -1) */ + + assert year != 0 + if (year < 0) + year++ + + /* move January and February to the end of the previous year */ + + if (month <= 2) + year--, month += 12 + new jdn = 365*year + year/4 - year/100 + year/400 + + (153*month - 457) / 5 + + day + 1721119 + + return jdn +} + +JulianToDate(jdn, &day, &month, &year) +{ + jdn -= 1721119 + + /* approximate year, then adjust in a loop */ + + year = (400 * jdn) / 146097 + while (365*year + year/4 - year/100 + year/400 < jdn) + year++ + year-- + /* determine month */ + + jdn -= 365*year + year/4 - year/100 + year/400 + month = (5*jdn + 457) / 153 + + /* determine day */ + + day = jdn - (153*month - 457) / 5 + + /* move January and February to start of the year */ + + if (month > 12) + month -= 12, year++ + + /* adjust negative years (year 0 must become 1 BC, or -1) */ + + if (year <= 0) + year-- +} + +``` + +--- + +`main` começa criando variáveis para armazenar dia, mês, ano e o JDN calculado. Em seguida lê a data (três chamadas a `getvalue`) e chama `DateToJulian` para obter o número. Depois de calcular, imprime a data digitada e o JDN correspondente. Foquemos agora em `DateToJulian`. + +Logo no início, a função incrementa o ano caso ele seja negativo —isso serve para lidar com a ausência do ano zero no calendário gregoriano próleptico. Em outras palavras, `DateToJulian` modifica os argumentos recebidos (mais tarde, altera também `month`). Dentro da função, o argumento se comporta como variável local e pode ser alterado; contudo, essas mudanças são locais a `DateToJulian`. A função `main` passa `d`, `m` e `y`, que são mapeados para `day`, `month` e `year`. Embora `DateToJulian` modifique `year` e `month`, ela não altera `y` e `m` em `main`, apenas suas cópias locais. Esse conceito é conhecido como “passagem por valor”. + +O exemplo usa nomes diferentes para as variáveis locais de `main` e `DateToJulian` justamente para tornar a explicação mais clara. Se renomearmos `d`, `m` e `y` para `day`, `month` e `year`, a situação continua a mesma: haverá dois conjuntos de variáveis com esses nomes —algo perfeitamente válido em pawn. + +`"Call by value" versus "Call by reference": 71` + +--- + +O restante de `DateToJulian` é aritmética pura, sem novidades para a linguagem. + +Voltando ao segundo trecho de `main`, vemos que agora ele solicita um número juliano e chama `JulianToDate` para encontrar a data correspondente. Essa função é interessante porque recebe um único argumento (o JDN) e precisa produzir três valores: dia, mês e ano. Mas uma função só pode retornar um valor —um `return` contém apenas uma expressão. Para resolver isso, `JulianToDate` pede explicitamente que as alterações feitas em alguns argumentos sejam copiadas de volta para as variáveis do chamador. Assim, em `main`, as variáveis que devem receber o resultado são passadas como argumentos. + +`JulianToDate` marca os argumentos que devem ser “copiados de volta” com o prefixo `&`. Argumentos com `&` são tratados de forma especial: em vez de passar apenas o valor, a função recebe acesso direto à variável original. Isso se chama “passagem por referência”, e o argumento se torna uma “referência”. + +Em outras palavras, se `main` passa `y` e `JulianToDate` o mapeia para `year`, qualquer alteração feita em `year` refletirá em `y`. Somente por meio de argumentos por referência uma função pode modificar diretamente uma variável declarada em outra função. + +Resumindo: se uma função retorna apenas um valor, use o `return`; se precisa retornar vários, use argumentos por referência. Ambos podem coexistir na mesma função —por exemplo, retornar o resultado principal via referência e um código de erro no `return`. + +Observação: muitas aplicações desktop usam conversões para e a partir de números julianos (ou variantes) para calcular intervalos entre datas ou descobrir a data exata daqui a 90 dias, por exemplo. + +### • Números racionais + +Até aqui lidamos apenas com números inteiros. Pawn também pode trabalhar com valores fracionários, chamados de “números racionais”, mas o suporte depende da aplicação hospedeira. + +Os racionais podem ser implementados como números de ponto flutuante ou de ponto fixo. Ponto flutuante é comum em cálculos gerais e científicos; ponto fixo é indicado para aplicações financeiras ou quando os erros de arredondamento devem ser inexistentes (ou, ao menos, previsíveis). O kit de ferramentas do pawn inclui módulos para ambos, com detalhes e compromissos explicados na documentação específica. Entretanto, o aplicativo host pode oferecer apenas um dos formatos, os dois ou nenhum∗. O programa abaixo exige que haja suporte a pelo menos um tipo de racional; caso contrário, ele não executa. + +Listing: c2f.p + +```c + +main() +{ + new Rational: Celsius + new Rational: Fahrenheit + print "Celsius\t Fahrenheit\n" + for (Celsius = 5; Celsius <= 25; Celsius++) + { + Fahrenheit = (Celsius * 1.8) + 32 + printf "%r \t %r\n", Celsius, Fahrenheit + } +} + +``` + +O programa converte uma tabela de graus Celsius para Fahrenheit. A primeira diretiva importa as definições necessárias para trabalhar com racionais. O arquivo `rational` carrega suporte a ponto flutuante ou ponto fixo, conforme o disponível. + +As variáveis `Celsius` e `Fahrenheit` são declaradas com a tag `Rational:` entre `new` e o nome da variável. Tags indicam a finalidade da variável, o uso permitido e, no caso específico de racionais, sua representação em memória. + +--- + +###### ∗ Actually, this is already true of all native functions, including all native functions that the examples in this manual use. + +`Tag names: 68` + +--- + +The Rational: tag tells the pawn parser that the variables Celsius and Fahrenheit +contain fractional values, rather than whole numbers. + +The equation for obtaining degrees Fahrenheit from degrees Celsius is + +°F = ⁹⁄₅ + 32 °C + +The program uses the value 1.8 for the quotient 9/₅. When rational number +support is enabled, pawn supports values with a fractional part behind +the decimal point. + +The only other non-trivial change from earlier programs is that the format +string for the printf function now has variable placeholders denoted with +“%r” instead of “%d”. The placeholder %r prints a rational number at +the position; %d is only for integers (“whole numbers”). + +I used the include file “rational” rather than “float” or “fixed” in an +attempt to make the example program portable. If you know that the host +application supports floating point arithmetic, it may be more convenient to +“#include” the definitions from the file float and use the tag Float: instead +of Rational —when doing so, you should also replace %r by %f in the call to +printf. For details on fixed point and floating point support, please see the +application notes “Fixed Point Support Library” and “Floating Point Support +Library” that are available separately. + +### • Strings + +pawn has no intrinsic “string” type; character strings are stored in arrays, with +the convention that the array element behind the last valid character is zero. +Working with strings is therefore equivalent with working with arrays. + +Among the simplest of encryption schemes is the one called “ROT13” — +actually the algorithm is quite “weak” from a cryptographical point of view. It +is most widely used in public electronic forums (BBSes, Usenet) to hide texts +from casual reading, such as the solution to puzzles or riddles. ROT13 simply +“rotates” the alphabet by half its length, i.e. 13 characters. It is a symmetric +operation: applying it twice on the same text reveals the original. + +--- + +Listing: rot13.p + +```c + +/* Simple encryption, using ROT13 */ + +main() +{ + printf "Please type the string to mangle: " + + new str[100] + getstring str, sizeof str + rot13 str + + printf "After mangling, the string is: \"%s\"\n", str +} + +rot13(string[]) +{ + for (new index = 0; string[index]; index++) + if (’a’ <= string[index] <= ’z’) + string[index] = (string[index] - ’a’ + 13) % 26 + ’a’ + else if (’A’ <= string[index] <= ’Z’) + string[index] = (string[index] - ’A’ + 13) % 26 + ’A’ +} + +``` + +No cabeçalho de `rot13`, o parâmetro `string` é declarado como array, mas sem tamanho —não há valor entre colchetes. Quando um tamanho é especificado no cabeçalho, ele precisa coincidir com o tamanho do argumento passado na chamada. Ao omitir, removemos essa restrição e permitimos que a função opere sobre arrays de qualquer comprimento. É preciso, então, ter outra forma de descobrir o tamanho (máximo). Para strings, basta procurar pelo terminador zero. + +O `for` que percorre a string é típico em rotinas de processamento de texto. Repare que a condição é `string[index]`. Em pawn, qualquer valor diferente de zero é considerado “true”; valores iguais a zero são “false”. Assim, quando `string[index]` chega a zero, o loop termina. + +O algoritmo ROT13 rotaciona apenas letras; dígitos, pontuação e caracteres especiais permanecem intactos. Além disso, maiúsculas e minúsculas são tratados separadamente. Dentro do loop, dois `if` filtram os caracteres relevantes. O encadeamento do segundo `if` no `else` do primeiro é um padrão comum para testar várias condições mutuamente exclusivas. + +No início deste capítulo falamos sobre passagem por valor e por referência. No caso de strings (ou arrays em geral), lembre-se de que pawn sempre passa arrays por referência. Isso economiza memória e melhora o desempenho, já que copiar uma estrutura grande apenas para passá-la por valor consumiria tempo e memória adicionais. + +--- + +`A function that takes an array as an argument and that does not change it, may mark the argument as “const”; see page 72` + +Por causa dessa regra, `rot13` pode modificar o parâmetro `string` sem precisar declará-lo explicitamente como argumento por referência. + +Outro ponto interessante são as condições dos `if`. A primeira usa `’a’ <= string[index] <= ’z’`, expressão verdadeira se, e somente se, `’a’ <= string[index]` **e** `string[index] <= ’z’`. Esse encadeamento de operadores relacionais é comum para expressar múltiplas comparações em uma única condição. + +Por fim, repare como o último `printf` em `main` usa `\"` para imprimir aspas duplas. Normalmente esse caractere encerraria a string literal; a sequência de escape insere a aspa no texto. + +Ainda falando de strings e arrays, o próximo programa divide uma frase em palavras e contabiliza quantas são. É um exemplo simples que apresenta alguns recursos novos da linguagem. + +Listing: wcount.p + +```c + +/* word count: count words on a string that the user types */ + +main() +{ + print "Please type a string: " + new string[100] + getstring string, sizeof string + + new count = 0 + + new word[20] + new index + for ( ;; ) + { + word = strtok(string, index) + if (strlen(word) == 0) + break + count++ + printf "Word %d: ’%s’\n", count, word + } + printf "\nNumber of words: %d\n", count +} + +strtok(const string[], &index) +{ + new length = strlen(string) + + /* skip leading white space */ + while (index < length && string[index] <= ’ ’) + index++ + /* store the word letter for letter */ + new offset = index /* save start position of token */ + new result[20] /* string to store the word in */ + while (index < length + && string[index] > ’ ’ + && index - offset < sizeof result - 1) + { + result[index - offset] = string[index] + index++ + } + result[index - offset] = EOS /* zero-terminate the string */ + return result +} + +``` + +--- + +`Relational operators: 107` + +`Escape sequence: 99` + +`main` primeiro mostra uma mensagem e lê a string digitada. Depois entra em um loop: escrever `for (;;)` cria um laço sem inicialização, incremento ou teste —um loop infinito, equivalente a `while (true)`. A diferença é que o analisador de pawn gera um aviso para `while (true)` (“expressão redundante; sempre verdadeira”), mas `for (;;)` passa sem alertas. + +Loops infinitos são úteis quando precisamos de um teste no meio —um híbrido entre `while` e `do...while`. Pawn não tem essa construção diretamente, mas podemos simulá-la com um loop infinito e um `break` condicional. No exemplo: + +- a palavra é extraída da string (código antes do teste); +- verificamos se há uma nova palavra; se não houver, saímos do loop (teste no meio); +- imprimimos a palavra e sua posição (código depois do teste). + +A linha `word = strtok(string, index)` (junto com a declaração de `word`) mostra que pawn permite atribuir arrays e retornar arrays de funções. O analisador verifica se o array retornado por `strtok` tem o mesmo tamanho/dimensão da variável que o recebe. + +`strlen` é uma função nativa; `strtok` não e precisa ser implementada. Ela foi inspirada na homônima de C/C++, mas não modifica a string original: em vez disso, copia palavra por palavra para um array local, que é retornado no final. + +--- + +### • Arrays e enumerações (dados estruturados) + +Em uma linguagem sem tipos, podemos atribuir funções diferentes a elementos específicos de um mesmo array. Pawn suporta constantes enumeradas com uma extensão que simula parte da funcionalidade das “structs” ou “records” de outras linguagens. + +O exemplo a seguir é maior do que os anteriores e demonstra outros recursos, como variáveis globais e parâmetros nomeados. + +```c + +/* Priority queue (for simple text strings) */ + +enum message +{ + text[40 char], + priority +} + +main() +{ + new msg[message] + + /* insert a few items (read from console input) */ + + printf "Please insert a few messages and their priorities; \ + end with an empty string\n" + for ( ;; ) + { + printf "Message: " + getstring .string = msg[text], .maxlength = 40, .pack = true + if (strlen(msg[text]) == 0) + break + printf "Priority: " + msg[priority] = getvalue() + if (!insert(msg)) + { + printf "Queue is full, cannot insert more items\n" + break + } + } + /* now print the messages extracted from the queue */ + printf "\nContents of the queue:\n" + while (extract(msg)) + printf "[%d] %s\n", msg[priority], msg[text] +} + +const queuesize = 10 +new queue[queuesize][message] +new queueitems = 0 +insert(const item[message]) +{ + /* check if the queue can hold one more message */ + if (queueitems == queuesize) + return false /* queue is full */ + + /* find the position to insert it to */ + new pos = queueitems /* start at the bottom */ + while (pos > 0 && item[priority] > queue[pos-1][priority]) + --pos /* higher priority: move one position up */ + + /* make place for the item at the insertion spot */ + for (new i = queueitems; i > pos; --i) + queue[i] = queue[i-1] + + /* add the message to the correct slot */ + queue[pos] = item + queueitems++ + + return true +} + +extract(item[message]) +{ + /* check whether the queue has one more message */ + if (queueitems == 0) + return false /* queue is empty */ + + /* copy the topmost item */ + item = queue[0] + --queueitems + + /* move the queue one position up */ + for (new i = 0; i < queueitems; ++i) + queue[i] = queue[i+1] + + return true +} + +``` + +--- + +`"for" loop: 113` + +`"enum" statement: 101` + +`"char" operator: 110` + +Near the top of the program listing is the declaration of the enumeration mes- +sage. This enumeration defines two constants: text, which is zero, and pri- +ority, which is 11 (assuming a 32-bit cell). The idea behind an enumeration +is to quickly define a list of symbolic constants without duplicates. By +default, every constant in the list is 1 higher than its predecessor and the very +first constant in the list is zero. However, you may give an extra increment for a +constant so that the successor has a value of 1 plus that extra increment. The + +text constant specifies an extra increment of 40 char. In pawn, char is an +operator, it returns the number of cells needed to +hold a packed string of the + +specified number of characters. Assuming a 32-bit cell and a 8-bit character, +10 cells can hold 40 packed characters. + +Immediately at the top of function main, a new array variable is declared with +the size of message. The symbol message is the name of the enumeration. It +is also a constant with the value of the last constant in the enumeration list +plus the optional extra increment for that last element. So in this example, +message is 12. That is to say, array msg is declared to hold 12 cells. + +Further in main are two loops. The for loop reads strings and priority values +from the console and inserts them in a queue. The while loop below +that +extracts element by element from the queue and prints the information on the +screen. The point to note, is that the for loop stores both the string and the +priority number (an integer) in the same variable msg; indeed, function main +declares only a single variable. Function getstring stores the message text +that you type starting at array msg[text] while the priority value is stored +(by an assignment a few lines lower) in msg[priority]. The printf function +in the while loop reads the string and the value from those positions as well. + +At the same time, the msg array is an entity on itself: it is passed in its +entirety +to function insert. That function, near the end, says “queue[queueitems] + += item”, where item is an array with size message and queue is a +two- +dimensional array that holds queuesize elements of size message. The decla- +ration of queue and queuesize are just above function insert. + +The example implements a “priority queue”. You can insert a number +of +messages into the queue and when these messages all have the same priority, +they are extracted from the queue in the same order. However, when +the +messages have different priorities, the one with the highest priority comes out +first. The “intelligence” for this operation is inside function insert: it +first +determines the position of the new message to add, then moves a few messages +one position upward to make space for the new message. Function extract +simply always retrieves the first element of the queue and shifts all remaining +elements down by one position. + +Note that both functions insert and extract work on two shared variables, +queue and queueitems. A variable that is declared inside a function, +like +variable msg in function main can only be accessed from within that function. +A “global variable” is accessible by all functions, and that variable is +declared +outside the scope of any function. Variables must still be declared before they +are used, so main cannot access variables queue and queueitems, but both +insert and extract can. + +--- + +Function extract returns the messages with the highest priority via its func- +tion argument item. That is, it changes its function argument by copying the +first element of the queue array into item. Function insert copies in the other +direction and it does not change its function argument item. In such a case, +it is advised to mark the function argument as “const”. This helps the pawn +parser to both check for errors and to generate better (more compact, quicker) code. + +A final remark on this latest sample is the call to getstring in function main: +note how the parameters are attributed with a description. The first param- +eter is labeled “.string”, the second “.maxlength” and the third “.pack”. +Function getstring receives “named parameters” rather than positional pa- +rameters. The order in which named parameters are listed is not important. +Named parameters are convenient in specifying —and deciphering— long pa- +rameter lists. + +--- + +`Named parameters: 74` + +### • Bit operations to manipulate ‘‘sets’’ + +A few algorithms are most easily solved with “set operations”, like +intersection, +union and inversion. In the figure below, for example, we want to design an +algorithm that returns us the points that can be reached from some other point +in a specified maximum number of steps. For example, if we ask it to +return the points that can be reached in two steps starting from B, the algorithm has +to return C, D, E and F, but not G because G takes three steps from B. + +Our approach is to keep, for each point in the graph, the set of other points +that it can reach in one step —this is the “next_step” set. We +also have a “result” set that keeps all points that we have found so far. We start by +setting the result set equal to the next_step set for the departure point. Now +we have in the result set all points that one can reach in one step. Then, for +every point in our result set, we create a union of the result set and the +next_step set for that point. This process is iterated for a specified number of loops. + +--- + +An example may clarify the procedure outlined above. When the departure +point is B, we start by setting the result set to D and E —these are the +points that one can reach from B in one step. Then, we walk through +the result set. The first point that we encounter in the set is D, and we check +what points can be reached from D in one step: these are C and F. So we add +C and F to the result set. We knew that the points that can be reached from +D in one step are C and F, because C and F are in the next_step set for + +D. So what we do is to merge the next_step set for point D into the result +set. The merge is called a “union” in set theory. That handles D. The original +result set also contained point E, but the next_step set for E is empty, so +no more point is added. The new result set therefore now contains C, D, E and F. + +![img](https://i.ibb.co/m9Dq7x2/image.png) + +A set is a general purpose container for elements. The only information that +a set holds of an element is whether it is present in the set or not. The order +of elements in a set is insignificant and a set cannot contain the same element + +multiple times. The pawn language does not provide a “set” data type or +operators that work on sets. However, sets with up to 32 elements can be +simulated by bit operations. It takes just one bit to store a “present/absent” +status and a 32-bit cell can therefore maintain the status for 32 set elements +—provided that each element is assigned a unique bit position. + +The relation between set operations and bitwise operations is summarized in +the following table. In the table, an upper case letter stands for a set and a +lower case letter for an element from that set. + +| concept | mathematical notation | pawn expression | +| ------------ | --------------------- | --------------- | +| intersection | A ∩ B | A & B | +| union | A ∪ B | A I B | +| complement | A | ~A | +| empty set | ε | 0 | +| membership | x ∈ A | (1 \<< x ) & A | + +--- + +To test for membership —that is, to query whether a set holds a particular +element, create a set with just one element and take the intersection. If the +result is 0 (the empty set) the element is not in the set. Bit numbering starts +typically at zero; the lowest bit is bit 0 and the highest bit in a 32-bit cell +is bit 31. To make a cell with only bit 7 set, shift the value 1 left by seven +—or in a pawn expression: “1 \<< 7”. + +Below is the program that implements the algorithm described earlier to find +all points that can be reached from a specific departure in a given number of +steps. The algorithm is completely in the findtargets function. + +Listing: set.p + +```c + +/* Set operations, using bit arithmetic */ + +main() +{ + enum (<<= 1) { A = 1, B, C, D, E, F, G } + new nextstep[] = + + { C | E, /* A can reach C and E */ + D | E, /* B " " D and E */ + G, /* C " " G */ + C | F, /* D " " C and F */ + 0, /* E " " none */ + 0, /* F " " none */ + E | F, /* G " " E and F */ + } + #pragma unused A, B + + print "The departure point: " + new source = clamp( .value = toupper(getchar()) - ’A’, + .min = 0, + .max = sizeof nextstep - 1 + ) + print "\nThe number of steps: " + new steps = getvalue() + + /* make the set */ + new result = findtargets(source, steps, nextstep) + printf "The points reachable from %c in %d steps: ", source+’A’, + steps + for (new i = 0; i < sizeof nextstep; i++) + if (result & 1 << i) + printf "%c ", i + ’A’ +} + +findtargets(source, steps, nextstep[], numpoints = sizeof nextstep) +{ + new result = 0 + new addedpoints = nextstep[source] + while (steps-- > 0 && result != addedpoints) + { + result = addedpoints + for (new i = 0; i < numpoints; i++) + if (result & 1 << i) + addedpoints |= nextstep[i] + } + return result +} + +``` + +--- + +The enum statement just below the header of the main function declares the +constants for the nodes A to G, but with a twist. Usually, the enum starts +counting from zero; here, the value of the first constant, A, is explicitly set to + +1. More noteworthy is the expression “(\<\<= 1)” between the enum keyword + and the opening brace that starts the constant list: it specifies a “bit + shifting” increment. By default, every constant in an enum list gets a value that is + 1 above its predecessor, but you can specify every successive constant + in an enumeration to have a value that is: + +_its predecessor incremented by any value (not just 1) —e.g., “(+= 5)”;_ + +_its predecessor multiplied by any value —e.g., “(_= 3)”;\_ + +_its predecessor bit-shifted to the left by any value —e.g., “(\<\<= 1)”;_ + +Note that, in binary arithmetic, shifting left by one bit amounts to the same +as multiplying by two, meaning that `(/*= 2)` and `(<<= 1)` do the same thing. + +When working with sets, a typical task that pops up is to determine the number +of elements in the set. A straightforward function that does this is below: + +Listing: simple bitcount function + +```c + +bitcount(set) +{ + new count = 0 + for (new i = 0; i < cellbits; i++) + if (set & (1 << i)) + count++ + return count +} + +``` + +With a cell size of 32 bits, this function’s loop iterates 32 times to check for +a single bit at each iteration. With a bit of binary arithmetic magic, we can +reduce it to loop only for the number of bits that are “set”. +That is, the following function iterates only once if the input value has only one bit set: + +Listing: improved bitcount function + +```c + +bitcount(set) +{ + new count = 0 + if (set) + do + count++ + while ((set = set & (set - 1))) + return count +} + +``` + +--- + +`“enum” statement: 101` + +`“cellbits” constant: 102` + +--- + +### • A simple RPN calculator + +The common mathematical notation, with expressions like “26 3 (5 + 2)”, +is known as the algebraic notation. It is a compact notation and +we have +grown accustomed to it. pawn and by far most other programming languages +use the algebraic notation for their programming expressions. The algebraic + +notation does have a few disadvantages, though. For instance, it occasionally +exige que deixemos explícita a ordem das operações com parênteses. A expressão no início desta seção poderia ser reescrita sem parênteses, mas à custa de quase dobrar o tamanho. Na prática, complementamos a notação algébrica com regras de precedência que dizem, por exemplo, que multiplicação vem antes de adição e subtração.∗ Essas regras reduzem muito a necessidade de parênteses, mas não a eliminam. Pior: quando o número de operadores cresce, lembrar a hierarquia de precedência e o nível de cada operador torna-se difícil —motivo pelo qual linguagens ricas em operadores, como APL, abandonam precedência e usam outra abordagem. + +Por volta de 1920, o matemático polonês Jan Łukasiewicz demonstrou que, colocando os operadores antes dos operandos em vez de entre eles, a precedência se tornava redundante e parênteses deixavam de ser necessários. Essa notação ficou conhecida como “notação polonesa”.† Mais tarde, Charles Hamblin sugeriu posicionar os operadores **após** os operandos, resultando na “notação polonesa reversa” (RPN). + +--- + +###### ∗ These rules are often summarized in a mnemonic like “Please Excuse My Dear Aunt Sally” (Parentheses, Exponentiation, Multiplication, Division, Addition, Subtraction) + +###### \* Polish Notation is completely unrelated to “Hungarian Notation” —which is just the habit of adding “type” or “purpose” identification warts to names of variables or functions + +--- + +`Algebraic notation is also called “infix” notation` + +`Reverse Polish Notation is also called “postfix” notation` + +--- + +O benefício dessa inversão é que os operadores aparecem na mesma ordem em que devem ser executados: ao lê-los da esquerda para a direita, realizamos as operações na mesma sequência. A expressão algébrica desta seção ficaria, em RPN: + +`26 3 5 2 + × −` + +Observando apenas os operadores, temos: primeiro uma soma, depois uma multiplicação e, por fim, uma subtração. Os operandos de cada operador são lidos da direita para a esquerda: os operandos de `+` são 5 e 2; os de `×` são o resultado da soma anterior e o valor 3; e assim por diante. + +É útil imaginar os valores sendo empilhados, com os operadores removendo um ou mais elementos do topo, efetuando a operação e devolvendo o resultado no topo. Ao percorrer a expressão em RPN, empilhamos 26, 3, 5 e 2 nessa ordem. O operador `+` remove 5 e 2 e empilha a soma, resultando em “26 3 7”. O operador `×` remove 3 e 7 e empilha o produto, deixando “26 21”. Por fim, o operador `−` subtrai 21 de 26 e empilha o valor único 5 —resultado final da expressão. + +RPN se popularizou porque é fácil de entender e de implementar (especialmente em calculadoras antigas). Ela também abre espaço para operadores com mais de dois operandos (como integrações) ou com mais de um resultado (como conversões entre coordenadas polares e cartesianas). + +Segue o programa principal de uma calculadora em notação polonesa reversa: + +Listing: rpn.p + +```c + +/* a simple RPN calculator */ +#include strtok +#include stack +#include rpnparse + +main() +{ + print "Type an expression in Reverse Polish Notation: " + new string[100] + getstring string, sizeof string + rpncalc string +} + +``` + +--- + +O programa principal em si é bem curto; ele apenas inclui o código de três arquivos auxiliares, cada um responsável por algumas funções que, combinadas, implementam a calculadora. Em programas maiores é comum distribuir a lógica em vários arquivos para facilitar a manutenção. + +`main` mostra um prompt e chama a nativa `getstring` para ler a expressão. Em seguida, invoca `rpncalc`, que faz o trabalho pesado. A implementação de `rpncalc` está em `rpnparse.inc`, reproduzida abaixo: + +Listing: rpnparse.inc + +````c + +/* main rpn parser and lexical analysis, part of the RPN calculator */ +#include +#include + +enum token +{ + t_type, /* operator or token type */ + Rational: t_value, /* value, if t_type is "Number" */ + t_word[20], /* raw string */ + +} + +const Number = ’0’ +const EndOfExpr = ’#’ + +rpncalc(const string[]) +{ + new index + new field[token] + for ( ;; ) + { + field = gettoken(string, index) + switch (field[t_type]) + { + case Number: + push field[t_value] + case ’+’: + push pop() + pop() + case ’-’: + push - pop() + pop() + case ’*’: + push pop() * pop() + case ’/’, ’:’: + push 1.0 / pop() * pop() + case EndOfExpr: + break /* exit "for" loop */ + default: + printf "Unknown operator ’%s’\n", field[t_word] + } + } + printf "Result = %r\n", pop() + if (clearstack()) + print "Stack not empty\n", red +} + +gettoken(const string[], &index) +{ + /* first get the next "word" from the string */ + new word[20] + word = strtok(string, index) + + /* then parse it */ + new field[token] + field[t_word] = word + if (strlen(word) == 0) + { + field[t_type] = EndOfExpr /* special "stop" symbol */ + field[t_value] = 0 + } + else if (’0’ <= word[0] <= ’9’) + { + field[t_type] = Number + field[t_value] = rationalstr(word) + } + else + { + field[t_type] = word[0] + field[t_value] = 0 + } + return field + +} + +```*** + +Essa calculadora usa suporte a números racionais, por isso `rpnparse.inc` inclui o arquivo `rational`. Quase todas as operações envolvendo racionais ficam escondidas na aritmética; as únicas referências diretas são o especificador de formato `%r` no `printf` ao final de `rpncalc` e a chamada `rationalstr` dentro de `gettoken`. + +O primeiro ponto curioso em `rpnparse.inc` é a declaração `enum`, na qual um elemento recebe uma tag (`t_value`) e outro define um tamanho (`t_word`). A função `rpncalc` declara a variável `field` como um array cujo tamanho é o símbolo dessa enumeração. Na prática, isso faz mais do que criar um array de 22 células: + +- O “índice” do array passa a ter a tag `token:`. Isso permite indexá-lo usando qualquer elemento da enumeração, mas impede o uso de valores com outras tags. Em outras palavras, `field[t_type]` é válido, enquanto `field[1]` gera diagnóstico. +- A tag da enumeração prevalece sobre a eventual tag do array. `field` em si não tem tag, mas `field[t_value]` recebe `Rational:`, já que o elemento `t_value` foi declarado assim. Isso permite criar arrays cujos elementos têm tags diferentes entre si. +- Quando um elemento da enumeração define um tamanho, a posição correspondente no array passa a ser tratada como um subarray. Em `rpncalc`, `field[t_type]` é uma célula, `field[t_value]` é outra, mas `field[t_word]` é um array unidimensional com 20 células. É por isso que a linha + + printf "Unknown operator ’%s’\n", field[t_word] + +where the format code %s expects a string —a zero-terminated array. + + + + + +`Rational numbers, see also the “Celsius to Fahrenheit” example on page page 16` + +`“enum” statement: 101` + +`Another example of an index tag: page 68` + + + +// came to here + + + + +*** + +If you know C/C⁺⁺ or Java, you may want to look at the switch statement. +The switch statement differs in a number of ways from the other languages +that provide it. The cases are not fall-through, for example, which in +turn means that the break statement for the case EndOfExpr breaks out of the +enclosing loop, instead of out of the switch. + +On the top of the for loop in function rpncalc, you will find the +instruction “field = gettoken(string, index)”. As already exemplified in the +wcount.p (“word count”) program on page 19, functions may return arrays. +It gets more interesting for a similar line in function gettoken: + +field[t_word] = word + +where word is an array of 20 cells and field is an array of 22 cells. +However, as the t_word enumeration field is declared as having a size of 20 cells, +“field[t_word]” is considered a sub-array of 20 cells, precisely matching the +array size of word. + +Listing: strtok.inc + +```c + +/* extract words from a string (words must be separated by white space) */ +#include + +strtok(const string[], &index) +{ + new length = strlen(string) + + /* skip leading white space */ + while (index < length && string[index] <= ’ ’) + index++ + + /* store the word letter for letter */ + new offset = index /* save start position of token */ + new result[20] /* string to store the word in */ + while (index < length + && string[index] > ’ ’ + && index - offset < sizeof result - 1) + { + result[index - offset] = string[index] + index++ + } + result[index - offset] = EOS /* zero-terminate the string */ + + return result +} + +```` + +--- + +`“switch” statement: page 115` + +--- + +Function strtok is the same as the one used in the wcount.p example. It is +implemented in a separate file for the rpn calculator program. Note that the +strtok function as it is implemented here can only handle words with up to 19 + +characters —the 20th character is the zero terminator. A truly general purpose +re-usable implementation of an strtok function would pass the destination +array as a parameter, so that it could handle words of any size. Supporting +both packed and unpack strings would also be a useful feature of a +general purpose function. + +When discussing the merits of Reverse Polish Notation, I mentioned that a +stack is both an aid in “visualizing” the algorithm as well as a +convenient method to implement an rpn parser. This example rpn calculator, uses +a stack with the ubiquitous functions push and pop. For error checking +and resetting the stack, there is a third function that clears the stack. + +Listing: stack.inc + +```c + +/* stack functions, part of the RPN calculator */ +#include + +static Rational: stack[50] +static stackidx = 0 + +push(Rational: value) +{ + assert stackidx < sizeof stack + stack[stackidx++] = value +} + +Rational: pop() +{ + assert stackidx > 0 + return stack[--stackidx] +} + +clearstack() +{ + assert stackidx >= 0 + if (stackidx == 0) + return false + stackidx = 0 + return true +} + +``` + +--- + +`wcount.p: page 19` + +--- + +The file stack.inc includes the file rational again. This is technically not +necessary (rpnparse.inc already included the definitions for rational number +support), but it does not do any harm either and, for the sake of code re-use, +it is better to make any file include the definitions of the libraries that it +depends on. + +Notice how the two global variables stack and stackidx are declared +as “static” variables; using the keyword static instead of new. Doing this makes +the global variables “visible” in that file only. For all other files in a +larger project, the symbols stack and stackidx are invisible and they cannot (ac- +cidentally) modify the variables. It also allows the other modules to declare +their own private variables with these names, so it avoids name clashing. + +Embora a calculadora RPN continue sendo um programa pequeno, estruturamos o código como se fosse maior para demonstrar vários elementos da linguagem. Em um cenário real, poderíamos implementá-la de maneira mais compacta. + +### • Event-driven programming + +Todos os exemplos deste capítulo até aqui seguiram um modelo “linear”: começam em `main` e o código decide o que fazer e quando pedir entrada. Esse modelo é fácil de entender e se encaixa bem na maioria das linguagens, mas não atende a muitas situações do mundo real. Frequentemente, o programa não pode simplesmente processar dados e solicitar entrada apenas quando for conveniente; é o usuário quem escolhe quando interagir, e o aplicativo deve estar pronto para responder rapidamente, independentemente do que estiver fazendo. + +Isso sugere que o programa deveria ser capaz de interromper o trabalho atual, realizar outra atividade e depois retomar a tarefa original. Nas primeiras implementações, isso era feito com sistemas multitarefa: uma thread cuidava das tarefas em segundo plano e outra ficava em loop aguardando entrada do usuário. Porém, essa solução é pesada. Um modelo mais leve para obter responsividade é o chamado “programação dirigida a eventos”. + +--- + +Nesse modelo, o programa divide tarefas longas em blocos curtos e, entre eles, fica disponível para processar eventos. Em vez de ficar “pollando” entradas, a aplicação hospedeira (ou outro subsistema) chama uma função associada ao evento —mas apenas quando ele ocorre. + +O evento mais comum é “entrada”. Porém, entradas não vêm apenas de usuários: podem chegar pacotes por cabos seriais, redes, temporizadores internos e outros dispositivos conectados. Muitos desses dispositivos simplesmente enviam dados; o recebimento é um evento, como uma tecla pressionada. Se você não tratar o evento, talvez alguns fiquem em uma fila interna, mas, quando ela enche, os demais são descartados. + +Pawn oferece suporte direto a esse modelo porque permite vários pontos de entrada. Em um programa linear, apenas `main` é o ponto inicial; em um programa dirigido a eventos, há uma função de entrada para cada evento capturado. Comparado ao modelo linear, esse estilo parece “de baixo para cima”: em vez de o programa chamar a aplicação hospedeira para decidir o próximo passo, é o host que invoca o script quando necessário, exigindo respostas rápidas. + +Como pawn não especifica uma biblioteca padrão, não há garantia de que determinada implementação forneça funções como `printf` ou `getvalue`. Embora se recomende que todas ofereçam ao menos uma interface mínima de console/terminal com essas funções, a disponibilidade depende da implementação. + +dependent. The same holds for the public functions —the entry points for a +script. It is implementation-dependent which public functions a host applica- +tion supports. The script in this section may therefore not run on your platform +(even if all previous scripts ran fine). The tools in the standard +distribution of +the pawn system support all scripts developed in this manual, provided that + +your operating system or environment supports standard terminal functions + +such as setting the cursor position. + +An early programming language that was developed solely for teaching the +concepts of programming to children was “Logo”. This dialect of LISP made +programming visual by having a small robot, the “turtle”, drive over the floor +under control of a simple program. This concept was then copied to moving +a (usually triangular) cursor of the computer display, again under control of a +program. A novelty was that the turtle now left a trail behind it, allowing you +to create drawings by properly programming the turtle —it became known as turtle graphics. + +--- + +`Public functions: 83` + +--- + +The term “turtle graphics” was also used for drawing inter- +actively with the arrow keys on the keyboard and a “turtle” for the current +position. This method of drawing pictures on the computer was briefly popular +before the advent of the mouse. + +Listing: turtle.p + +```c + +@keypressed(key) +{ + /_ get current position */ + new x, y + wherexy x, y + + /_ determine how the update the current position */ + switch (key) + { + case ’u’: y-- /_ up */ + case ’d’: y++ /_ down */ + case ’l’: x-- /_ left */ + case ’r’: x++ /_ right */ + case ’\e’: exit /_ Escape = exit */ + } + + /_ adjust the cursor position and draw something */ + moveturtle x, y +} + +moveturtle(x, y) +{ + gotoxy x, y + print ’/*’ + gotoxy x, y +} + +``` + +The entry point of the above program is @keypressed —it is called on a +key press. If you run the program and do not type any key, the +function @keypressed never runs; if you type ten keys, @keypressed runs ten times. +Contrast this behaviour with main: function main runs immediately after you +start the script and it runs only once. + +It is still allowed to add a main function to an event-driven program: the main +function will then serve for one-time initialization. A simple addition to this +example program is to add a main function, in order to clear the +console/ +terminal window on entry and perhaps set the initial position of the “turtle” +to the centre. + +Support for function keys and other special keys (e.g. the arrow keys) is highly +system-dependent. On ANSI terminals, these keys produce different codes +than in a Windows “DOS box”. + +--- + +In the spirit of keeping the example program +portable, I have used common letters (“u” for up, “l” for left, etc.). This +does not mean, however, that special keys are beyond pawn’s capabilities. + +In the “turtle” script, the “Escape” key terminates the host application through +the instruction exit. For a simple pawn run-time host, this will indeed work. +With host applications where the script is an add-on, or +host-applications + +that are embedded in a device, the script usually cannot terminate the host application. + +### • Múltiplos eventos + +As vantagens do modelo dirigido a eventos para construir programas reativos ficam claras quando há diversos eventos. Na prática, ele só faz sentido se houver mais de um ponto de entrada; se seu script lida com apenas um evento, um loop de polling seria suficiente. Quanto mais eventos precisamos tratar, mais difícil fica usar o modelo linear. O script a seguir implementa um chat bem simples com dois eventos: envio e recebimento. Ele permite que usuários em rede (ou outro meio) troquem mensagens de uma linha. + +O script depende de a aplicação hospedeira fornecer funções nativas/públicas para enviar e receber datagramas e reagir a teclas digitadas. O método de envio (serial, TCP/IP etc.) fica a cargo do host. As ferramentas da distribuição padrão do pawn usam TCP/IP e permitem um modo de “broadcast” para que mais de duas pessoas conversem. + +--- + +Listing: chat.p + +```c + +#include + +@receivestring(const message[], const source[]) +printf "[%s] says: %s\n", source, message + +@keypressed(key) +{ + static string[100 char] + static index + + if (key == ’\e’) + exit /* quit on ’Esc’ key */ + + echo key + + if (key == ’\r’ || key == ’\n’ || index char == sizeof string) + { + string{index} = ’\0’ /* terminate string */ + sendstring string + + index = 0 + string[index] = ’\0’ + } + else + string{index++} = key +} + +echo(key) +{ + new string[2 char] = { 0 } + string{0} = key == ’\r’ ? ’\n’ : key + printf string +} + +``` + +Grande parte do script se ocupa de juntar os caracteres digitados em uma string e enviá-la quando o usuário pressiona Enter. A tecla Escape encerra o programa. A função `echo` gera um feedback visual do que é digitado: monta uma string com terminador zero a partir da tecla e a imprime. + +Apesar da simplicidade, o script demonstra uma característica interessante: não há ordem rígida para enviar ou receber mensagens —não existe o esquema pergunta–resposta em que cada host “espera a vez”. Uma mensagem pode chegar enquanto o usuário ainda está digitando.∗ + +### • Programação com estados + +Em um programa dirigido a eventos, cada evento chega isoladamente e recebe uma resposta isolada. Às vezes, porém, um evento faz parte de um fluxo sequencial que deve ser tratado na ordem correta. Protocolos de transferência de dados em linhas seriais são um exemplo: cada evento pode carregar um comando, um pedaço de arquivo, um reconhecimento ou outro sinal do protocolo. + +--- + +###### ∗ As this script makes no attempt to separate received messages from typed messages (for example, in two different scrollable regions), the terminal/console will look confusing when this happens. With an improved user-interface, this simple script could indeed be a nice message-base chat program + +--- + +Para que esse fluxo faça sentido, o programa precisa seguir um protocolo rigoroso de “handshake”. Isso significa que ele deve reagir a cada evento considerando o histórico recente de eventos anteriores e as respostas enviadas. Ou seja, o tratamento de um evento pode definir a “condição” ou “contexto” em que os próximos serão processados. + +Uma abstração simples e eficaz para construir sistemas reativos que seguem protocolos parcialmente sequenciais é o autômato ou máquina de estados. Como o número de estados costuma ser finito, falamos em Máquinas de Estados Finitos (MEF). No autômato, o contexto do evento é o estado atual. Um mesmo evento pode ser tratado de maneiras diferentes dependendo desse estado e, em resposta, o autômato pode mudar para outro estado —essa mudança é chamada transição. + +Autômatos são comuns tanto em software quanto em dispositivos mecânicos (o tear de Jacquard é um exemplo clássico). Com um número finito de estados, são determinísticos (comportamento previsível) e fáceis de implementar a partir de um diagrama de estados. + +![State diagram](https://i.ibb.co/k3kWVvy/image.png) + +In a state diagram, the states are usually represented as circles or +rounded rectangles and the arrows represent the transitions. As transitions are +the response of the automaton to events, an arrow may also be seen as an event “that does something”. + +--- + +An event/transition that is not defined in a particular +state is assumed to have no effect —it is silently ignored. A filled dot +represents the entry state, which your program (or the host application) must set in start- +up. It is common to omit in a state diagram all event arrows that drop back +into the same state, but here I have chosen to make the response to all events explicit. + +O diagrama acima corresponde à análise de comentários iniciados por `/*` e terminados em `*/`. Há estados para texto normal, para texto dentro do comentário e dois estados intermediários para entrada e saída. O autômato foi pensado para processar caracteres digitados pelo usuário, então reage apenas a eventos de tecla. Na prática há um único evento (“tecla pressionada”); as transições dependem do parâmetro (a própria tecla). + +Pawn oferece suporte nativo a autômatos e estados. Cada função∗ pode ter um ou mais estados associados. A linguagem também permite múltiplos autômatos, e cada estado pertence a um autômato específico. + +O script abaixo implementa o diagrama descrito (usando um autômato anônimo). Para destacar o texto normal dos comentários, cada tipo recebe uma cor diferente. + +Listing: comment.p + +```c + +/* parse C comments interactively, using events and a state machine */ + +main() + state plain + +@keypressed(key) +{ + state (key == ’/’) slash + if (key != ’/’) + echo key +} + +@keypressed(key) +{ + state (key != ’/’) plain + state (key == ’/*’) comment + echo ’/’ /* print ’/’ held back from previous state */ + if (key != ’/’) + echo key +} + +@keypressed(key) +{ + echo key + state (key == ’/*’) star +} + +@keypressed(key) +{ + echo key + state (key != ’/*’) comment + state (key == ’/’) plain +} + +echo(key) + printchar key, yellow + +echo(key) + printchar key, green + +printchar(ch, colour) +{ + setattr .foreground = colour + printf "%c", ch +} + +``` + +--- + +###### ∗ With the exception of “native functions” and user-defined operators. + +--- + +`main` define o estado inicial como `plain` e termina; toda a lógica é dirigida a eventos. Quando uma tecla chega no estado `plain`, verificamos se é uma barra e, dependendo disso, imprimimos ou não o caractere. A interação entre os estados `plain` e `slash` mostra uma característica típica dos autômatos: é preciso decidir como reagir no momento em que o evento acontece, sem “olhar à frente” nem desfazer respostas anteriores. Em sistemas dirigidos a eventos, geralmente não sabemos qual será o próximo evento nem quando virá, e o que fizermos agora dificilmente poderá ser anulado depois. + +Neste caso, quando uma barra chega, ela **pode** indicar o início de um comentário (`/*`), mas não necessariamente. Não dá para decidir de imediato em que cor imprimir o caractere, então o mantemos “em espera”. Note que não declaramos nenhuma variável global para isso; na verdade, além dos parâmetros, não há variáveis declaradas. A informação de que há um caractere pendente está implícita no estado do autômato. + +Como mostra o script, mudanças de estado podem ser condicionais. A condição é opcional, e também podemos usar `if/else` tradicionais para alterá-los. + +A dependência de estado não se restringe às funções de evento. Outras funções podem declarar estados, como `echo` faz. Quando várias situações compartilham a mesma implementação, basta escrever uma função e listar todos os estados aplicáveis. Em `echo` existem duas versões para cobrir os quatro estados.† + +Um autômato deve estar preparado para tratar todos os eventos em qualquer estado. Normalmente ele não controla quais eventos chegam nem quando, portanto ignorá-los em certo estado pode levar a falhas. Muitas vezes alguns eventos fazem sentido somente em poucos estados e, nos demais, deveriam disparar erros ou uma rotina de “reset”. Para evitar listar manualmente todos os estados inválidos, podemos omitir o conteúdo entre os colchetes angulares: assim, a função atende a todos os estados que não têm implementação específica. Por exemplo, poderíamos declarar `echo(key) <>` em vez de listar explicitamente cada estado (mas apenas em uma das implementações). + +Existe um autômato anônimo predefinido. Se o programa contiver mais de um, precisamos mencionar seu nome tanto no qualificador quanto no comando `state`. Basta colocar o nome do autômato antes do estado, separados por `:` —por exemplo, `parser:slash` refere-se ao estado `slash` do autômato `parser`. Uma função só pode pertencer a um autômato; podemos reutilizar a mesma implementação em vários estados desse autômato, mas não em estados de autômatos diferentes. + +### • Funções `entry` e teoria de autômatos + +As máquinas de estados e a própria teoria dos autômatos têm origem no design mecânico e em circuitos elétricos/pneumáticos de comutação (com relés, não transistores). Exemplos típicos são aceitadores de moedas, semáforos e centrais de comunicação. Nesses sistemas, robustez e previsibilidade são fundamentais, e descobriu-se que esses objetivos são alcançados com mais facilidade quando as ações (saídas) estão ligadas aos estados em vez dos eventos (entradas). + +--- + +###### \* A function that has the same implementation for all states, does not need a state classifierat all —see printchar. + +--- + +![pcl](https://i.ibb.co/PYnBGS9/image.png) + +###### Figure 1: Pedestrian crossing lights + +--- + +Ao entrar em um estado, opcionalmente realizamos alguma ação; os eventos apenas provocam a mudança de estado, mas não executam operações por conta própria. + +Em um semáforo para pedestres, as luzes dos veículos e dos pedestres precisam ficar sincronizadas. Certamente, exibir verde para os carros e “andar” para os pedestres seria desastroso —o mesmo vale para amarelo/andar. Restam quatro combinações possíveis. O diagrama a seguir descreve o processo: tudo começa com um botão e é controlado por temporizadores. + +![pcl](https://i.ibb.co/9wNR3ry/image.png) + +When the state red/walk times out, the state cannot immediately go back to +green/wait, because the pedestrians that are busy crossing the road at +that moment need some time to clear the road —the state red/wait +allows for this. + +--- + +Para fins de demonstração, este semáforo tem um extra: se o pedestre aperta o botão enquanto a luz já está vermelha para os carros, o tempo de travessia é prolongado. Ou seja, se estamos em `red/wait` e alguém pressiona o botão, voltamos para `red/walk`. A caixa que envolve esses estados no diagrama é apenas uma convenção visual; poderíamos ter desenhado duas setas voltando a `red/walk`. O código abaixo segue a mesma convenção. + +Na implementação em pawn, as funções de evento sempre têm uma única instrução: ou mudam de estado ou não fazem nada. Os eventos que não provocam mudanças não aparecem no diagrama, mas precisam ser tratados no script —daí as funções “fallback” vazias. + +A saída (neste caso, apenas mensagens no console) é feita nas funções especiais `entry`. Podemos vê-las como “main” de cada estado: são chamadas automaticamente quando o estado correspondente é ativado. Note que elas também são executadas em reentradas, ou seja, quando mudamos para o mesmo estado em que já estávamos. + +Listing: traffic.p + +```c + +/* traffic light synchronizer, using states in an event-driven model */ +#include