3 pontos por GN⁺ 2024-10-25 | 1 comentários | Compartilhar no WhatsApp
  • Pretty C é uma nova linguagem de scripting compatível com C que adiciona recursos de conveniência a programas em C, como tipagem dinâmica, iteração genérica e rastreamento de recursos
  • A forma de uso é como uma biblioteca header-only que inclui apenas pretty.h, podendo ser usada junto com arquivos C e bibliotecas C existentes
  • Os recursos oferecidos incluem aliases como string, any e nil, inferência de tipos com var/let, saída genérica com print/println, comparação genérica com equal e macros de operador ternário mais legíveis
  • Para reduzir repetição em laços e operações de memória, fornece uma sintaxe baseada em macros como foreach, forthese, fortimes, forrange, new, vector, delete, with, lambda e try/catch
  • Alguns recursos dependem de condições como GCC, Clang, C23+ e C11+, e variáveis agrupadas com with são void *, então pode ser necessário converter o tipo antes de usar

Objetivos e forma de uso do Pretty C

  • Pretty C é uma linguagem de scripting compatível com C que adiciona a programas em C tipagem dinâmica, iteração genérica, rastreamento de recursos e várias conveniências de sintaxe
  • Mantém compatibilidade retroativa com C existente e com bibliotecas C
  • É inspirado em Lua, Python, JavaScript e Lisp
  • A instalação pode ser feita clonando o repositório ou copiando apenas o arquivo pretty.h
    • Pode ser incluído em qualquer arquivo C com #include "pretty.h"
    • Também pode ser usado definindo o caminho do Pretty C no include path

Headers incluídos por padrão e macros simples

  • Pretty C inclui headers padrão para permitir o uso imediato de tipos comuns e expressões alternativas de operadores
    • stdbool.h: true, false, bool
    • stdint.h: tipos inteiros de largura fixa como uint64_t
    • iso646.h: operadores alternativos como and no lugar de && e or no lugar de ||
  • Também fornece macros simples frequentemente definidas
    • max, min: maior e menor valor entre dois números
    • len: comprimento de array
    • default: fornece um valor de fallback
    • limit: limita um valor a uma faixa
    • between: verifica se um número está dentro de uma faixa
    • divisible: verifica se é divisível por outro número

Tipos e aliases

  • Pretty C fornece aliases de tipo curtos para tipos de C
    • string é char*
    • byte é char
    • bytes é char*
    • any é void*
    • Também oferece formas abreviadas de inteiros como uchar, ushort, uint, ulong
  • Também inclui aliases de sintaxe influenciados por Lua e Lisp
    • eq, is: ==
    • bitnot, bitxor: aliases para operações de bit
    • success, fail, failure: aliases para o padrão success == 0
    • below, above, upto, downto: operadores de comparação
    • even, odd, positive, negative, zero, empty: predicados para números e dados
    • nil: NULL
    • until: while negado
    • elif: else if
    • ifnt, elifnt: expressões da família if(!...)
    • repeat: alias estilo Lua para do
    • done, finish, pass: aliases para break e continue
    • always, forever, loop, indefinitely: expressões para loop infinito
    • never, comment: expressões que não executam código, mas ainda permitem análise pelo compilador

Inferência de tipos, saída, comparação e ternário

  • A inferência de tipos pode ser usada com GCC, Clang ou C23+
    • Exemplos fornecidos: var t = time(0);, let lt = localtime(&t);, local at = asctime(lt);
  • A saída genérica está disponível em C11+
    • print exibe o valor passado
    • println adiciona uma quebra de linha após a saída
  • A comparação genérica de igualdade também é oferecida como recurso de C11+
    • Comparações de string como equal("NA", line)
    • Inclui exemplos como equal(0.3, 0.2 + 0.1)
  • O operador ternário pode se tornar mais legível com macros baseadas em texto
    • when se expande para string vazia para fins de legibilidade
    • unless se expande para not, funcionando como forma negativa de when
    • then é ?
    • other e otherwise são :
    • only é usado quando a cláusula otherwise não é necessária
    • otherwhen é usado para encadear a próxima condição

Macros de iteração

  • Pretty C abstrai padrões frequentes de loop for com macros
  • foreach (var, type, length, ...)
    • Percorre um array ou bloco de memória
    • Em cada iteração, var se torna um ponteiro para o elemento correspondente do array
    • Como é um ponteiro, o elemento pode ser modificado in-place quando necessário
  • forthese (var, type, ...)
    • Percorre os argumentos variáveis passados, vinculando cada valor a var do tipo type
  • fortimes (var, times)
    • Reduz o padrão comum de repetir de 0 até um número positivo de vezes
  • forrange (var, init, target)
    • Percorre um intervalo numérico de init até target
    • init e target podem ser quaisquer inteiros signed ou unsigned
    • Se init for maior que target, o passo da iteração diminui
  • forrangeby (var, type, init, target, by)
    • Percorre var do tipo especificado de init até target em passos de by

Utilitários de alocação e de bloco

  • As macros de alocação simplificam padrões comuns de alocação de memória em um formato inspirado em C++
    • new (type, ...): cria structs e outros objetos em uma forma parecida com o new do C++
    • vector (length, type, ...): simplifica o padrão de alocar um array com comprimento e tipo definidos, além de preencher o conteúdo inicial
    • delete (...): equivalente a free, mas útil para quem prefere um nome ao estilo C++
  • Os utilitários de bloco fornecem macros para binding local, execução adiada e operações em nível de bloco
    • lambda (ret, name, ...): permite usar em C formas de função aninhada, lambda e closure com GCC, Clang ou C++
    • with (var, close, ...): fornece antecipadamente a função de liberação de recursos close, sendo útil para objetos alocados dinamicamente ou file handles
      • Como desvantagem, a variável vinculada var é void *, então pode ser necessário convertê-la para o tipo desejado antes de usar
    • defer: foi removido, e é indicado usar o novo defer agora disponível
    • try e catch: permitem escrever tratamento de erros em C de forma mais sofisticada
    • NOERR, NOERROR: fornecidos para conveniência em switch-case de erro

Como contribuir

1 comentários

 
GN⁺ 2024-10-25
Comentários do Hacker News
  • A tradição de criar linguagens específicas de domínio dentro de C é antiga
    Stephen Bourne queria escrever o shell em ALGOL, então martelou obstinadamente o pré-processador de C até fazê-lo parecer com a linguagem que ele preferia
    https://www.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd/sh...

  • Queria ver alguém explicar se isso é uma piada ou se o autor está realmente confuso
    Nada faz sentido. Não é “scripting”, diz que introduz “tipagem forte”, mas não faz absolutamente nada com tipos, e os apelidos de operadores supostamente inspirados em Lua e Lisp na verdade não existem em nenhuma das duas linguagens. Mas também não parece claramente uma paródia, então é realmente confuso

    • Parece ser sério mesmo. Afinal, diz “Jogue fora Lua, Python, JavaScript, Ruby e dezenas de outras linguagens. Pretty C é a linguagem de scripting definitiva, ultrarrápida e ainda por cima com tipagem forte!!”
    • Não acho nem um pouco que o autor esteja confuso. Claro, ser confuso em si não tem problema
  • Diz que é uma linguagem de scripting, mas ainda é preciso compilar o programa. Que vaia
    Se acoplar o CINT(https://root.cern.ch/root/html534/guides/users-guide/CINT.ht...), dá para executar na hora e ter REPL também

    • Se o objetivo deste repositório é causar sofrimento, acho que bastaria adicionar um shebang ao arquivo para torná-lo executável [0]
      Vi há algum tempo um post de blog que explicava em detalhes como ./foo funciona e como arquivos ELF são executados. Talvez desse para registrar programas .c da mesma forma, para compilar e executar em seguida
      [0] https://gist.github.com/jdarpinian/1952a58b823222627cc1a8b83...
    • Eu preferiria usar tcc [0]. É muito mais leve do que aquele monstro e é C, não C++
      [0] https://bellard.org/tcc/tcc-doc.html
    • A CERN agora usa cling (https://github.com/root-project/cling)
    • Quem foi que decidiu que uma linguagem de scripting não pode ser compilada? E Clang-REPL é outra forma de tornar isso amigável a REPL
  • “Fornece açúcar sintático suficiente para causar um ataque cardíaco diabético em desenvolvedores C.”
    Adorei essa frase

  • Estou vendo algumas coisas para adicionar ao CNoEvil[0] e ao ogw[1]. Parece que toda vez que esse projeto ressurge a cada poucos meses, sempre aparece mais material
    [0] https://git.sr.ht/~shakna/cnoevil3/
    [1] https://git.sr.ht/~shakna/ogw

    • “Pega inúmeras ideias ruins e as amassa dentro de uma monstruosidade repulsiva.”
      É uma frase pela qual eu sinceramente acabo me apaixonando do fundo do coração
  • Isso está repetindo o mesmo erro do Python 2. Strings e bytes não são o mesmo tipo, e não deveriam ser tratados assim

    • Fico pensando qual deveria ser o tipo do texto de shell, ou seja, os valores em argv ou a saída de subprocessos
      Aquilo pode conter lixo arbitrário, então não é uma string UTF-8 válida, mas ferramentas como awk e grep são usadas em todo lugar
      Dá para considerar strings e bytes como o mesmo tipo geral, mas acho útil dar internamente um tipo diferente a bytes UTF-8 válidos quando isso fizer sentido. Rust lida com isso razoavelmente bem com OsString e String
    • Como este projeto é o pretty.c e diz ter a intenção de ser retrocompatível com C e todas as bibliotecas, existe escolha mais sensata do que manter as strings multibyte de C?
      https://en.cppreference.com/w/c/string/multibyte
    • Discordo. Esse princípio parte de todas as seguintes premissas: dados de string são codificados corretamente, existe apenas uma codificação de string (normalmente UTF-8), os dados de string devem ser validados no momento em que são gerados, nunca se deve permitir cortar um code point lógico no meio, não se deve aplicar operações de string a bytes “inválidos”, e a codificação correta é o começo e o fim da validação
      Nada disso é sempre verdade. É útil encapsular sequências de bytes validadas em um tipo que só possa ser construído por validação, mas, uma vez feito isso, Utf8String e EmailAddress passam a ser praticamente da mesma categoria. Não há motivo para dar tratamento especial apenas à codificação no sistema de tipos
    • Depende de como você define “string”
      Se for “texto legível por humanos”, concordo que strings não são a mesma coisa que um array arbitrário de bytes. Mas muitas linguagens não impõem essa definição
  • Isso me lembra de uma base de código em C++ que precisei inspecionar certa vez. Tudo parecia ter sido escrito como se fosse Java: camel case em tudo, getter/setter para todas as variáveis de classe e interfaces espalhadas por toda parte

    • Você ainda não viu nada. Basta olhar o código-fonte do Bourne shell do Unix Seventh Edition
      https://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/cmd...
      É difícil acreditar que não seja ALGOL
    • Camel case, getter/setter e interfaces em muitos casos não são tão diferentes de diretrizes. O código do Windows, por exemplo, é assim, embora claro não para toda variável. Grande parte do design do Java também foi copiada do C++
    • Já vi bases de código parecidas escritas por pessoas que usaram Java por tempo demais. Havia até uma classe String própria que era só um wrapper de std::string, mas com métodos no estilo Java
    • Ainda bem que parece que não usavam MSVC
      https://learn.microsoft.com/en-us/cpp/cpp/property-cpp?view=...
    • Já passei por algo assim também, e ainda por cima as strings eram passadas como no Java, em f(String *), então o código C++ ficava tipo f(new String("Hello")
  • Se isso te parece interessante, talvez você também goste de libcello.h
    https://www.libcello.org

    • Sim, essa foi uma das inspirações
  • Os nomes dos tipos são bons, e a escolha de macros de funções embutidas como min é perfeita. Também gosto de Len
    Os operadores booleanos nomeados talvez sejam um pouco demais, mas vale experimentar, e o operador ternário já é difícil de ler por natureza, então há espaço para melhorar. Não fiquei totalmente convencido por todas as definições de repetição, mas algumas fazem sentido. O rastreamento de recursos é impressionante, e a forma for... é um pouco feia, então talvez desse para dar outro nome.
    No geral, é uma tentativa bem sólida. Eu daria algo como 8/10 para o design. O jeito como isso foi esboçado com macros de C é realmente elegante e de fato parece bom código. Não sei se eu usaria pessoalmente. É uma linguagem nova, e eu já gosto de C. Ainda assim, a forma de implementação é muito clara, então pode ajudar as pessoas a aprender C e pensar sobre design de linguagens

    • Não é preciso usar tudo. Meus projetos em geral usam só booleanos, len(), max/min e algumas macros de operadores. É que eu ainda não precisei muito das outras funcionalidades saborosas. Então seria legal experimentar nem que seja só com algumas macros de operadores
  • Se existe ifnt para if(!...), então unless parece mais legível do que ifnt

    • Outra questão de nomenclatura é chamar o loop infinito for(;;) de always
      Em outras linguagens já vi loop. Mas o Qt chama isso de forever, e isso é realmente bonito. É bem a cara do Qt
    • Por outro lado, ifnt é divertido de falar em voz alta
    • Sim. Mas deixei unless para a condição ternária. De qualquer forma, ele é mais útil ali
      Ah, espera, é o John Tromp. O criador de BLC! Sou fã