A estranha estrutura da sintaxe lexical
(justine.lol)- Ao lidar com 42 linguagens para criar um novo realce de sintaxe para o llamafile, ficou claro que até o lexing simples esconde exceções por linguagem e gramáticas antigas em toda parte
- A implementação usa C++ e GNU gperf para acelerar a busca de palavras-chave, e o tratamento centrado em strings, comentários e palavras-chave é resolvido com um laço
fore uma máquina de estados finitos baseada emswitch - O trigraph de C, os terminadores de linha
u2028eu2029do JavaScript, os heredocs de Shell, Perl e Ruby, e a interpolação de strings em Kotlin, Scala, TypeScript e Swift reaparecem como casos difíceis de tratar apenas com lexing - Em linhas de código de implementação, FORTH fica em 125 linhas, enquanto Ruby chega a 1042; em Ruby, o operador
<<, heredoc e backquote entram em conflito, o que torna especialmente difícil realçar sem parsing - O novo realçador do llamafile foi demonstrado no Windows 10 com o Meta LLaMA 3.2 3B Instruct e também pode ser executado em macOS, Linux, FreeBSD e NetBSD, diferenciando-se do ollama por oferecer realce de sintaxe
Motivo para criar o realçador do llamafile
- Para criar um novo realce de sintaxe para o llamafile, foram estudadas 42 linguagens de programação ao longo de um mês
- O suporte inclui Ada, Assembly, BASIC, C, C#, C++, COBOL, CSS, D, FORTH, FORTRAN, Go, Haskell, HTML, Java, JavaScript, Julia, JSON, Kotlin, ld, LISP, Lua, m4, Make, Markdown, MATLAB, Pascal, Perl, PHP, Python, R, Ruby, Rust, Scala, Shell, SQL, Swift, Tcl, TeX, TXT, TypeScript e Zig
- Essa lista cobre a maior parte do TIOBE Index, mas Scratch foi excluída do escopo porque usa blocos em vez de texto
Método de implementação: gperf e máquina de estados finitos
- O maior gargalo em um realçador de sintaxe básico costuma aparecer ao determinar se um token é uma palavra-chave, quando aumentam as comparações repetidas de strings
- Foi usado C++ com GNU gperf para gerar uma tabela hash perfeita
- Um exemplo de entrada do
gperfdefine constantes de Java comotrue,falseenullcomo palavras-chave - O arquivo C gerado pelo
gperfpode criar uma função de hash que considera apenas um único caractere para consultas sem colisão
- Um exemplo de entrada do
- O realce de C consegue processar cerca de 4.000 palavras-chave a 35 MB/s graças ao gperf
- O restante do processamento, em sua maior parte, pode ser resolvido com uma máquina de estados finitos
- Dá para criar um realçador básico apenas com laços
foreswitch, semflex,bisonnemragel - Se o foco estiver em strings, comentários e palavras-chave, em geral o tratamento fica no nível de lexing
- Para destacar elementos como nomes de função em C, pode ser necessário parsing de verdade
- Dá para criar um realçador básico apenas com laços
- highlight_ada.cpp é usado como implementação de exemplo
Demo do llamafile e ambiente de uso
- O novo realçador e a interface de chatbot melhoraram a usabilidade do llamafile, e o realce de sintaxe vira um diferencial em relação ao ollama
- A demo foi executada no Windows 10 com o modelo Meta LLaMA 3.2 3B Instruct
- Esse llamafile também pode ser executado em macOS, Linux, FreeBSD e NetBSD
- Com a melhora da qualidade de modelos de pesos abertos como gemma 27b it, diminui o incentivo para usar Claude
C: exceções de lexing diferentes da imagem de linguagem simples
- Apesar da impressão de simplicidade, C tem elementos lexicais bastante peculiares
- O trigraph permite substituir caracteres como
#,[,\\,^,{,|,},~por??=,??(,??/,??),??',??<,??!,??>,??-- Ele foi removido no padrão C23, mas compiladores provavelmente continuarão oferecendo suporte por causa de software legado
- Um bom realçador de sintaxe também precisa tratar essa gramática
- O universal character de C permite representar identificadores Unicode em código-fonte ASCII, como em
int \\uFEB2 = 1;- O GCC gera erro se o caractere não estiver em um dos planes Unicode específicos permitidos pelo comitê do padrão
- O comentário de uma linha em C pode se estender por várias linhas se houver uma barra invertida no fim da linha
- Linguagens que aceitam escape com barra invertida no código-fonte, como Perl, Ruby e Shell, não suportam esse comportamento ao estilo C
- Tcl e GNU Make parecem suportar esse comportamento
- Emacs e Pygments às vezes tratam isso errado, enquanto o Vim aparentemente sempre acerta o tratamento da barra invertida
- C também tem a diretiva de pré-processador nula
- Arquivos
.cdo código-fonte v6 antigo frequentemente começam com uma linha contendo apenas# - Isso ainda é código válido hoje e pode ser usado para fazer o pré-processador remover certos comentários, mesmo quando
cc -C -Epede preservação deles
- Arquivos
Sintaxe de comentários: Haskell e D
- Em C, não é possível aninhar comentários de múltiplas linhas dentro de outros comentários de múltiplas linhas
- Haskell oferece suporte a comentários aninhados na forma
{- ... {- ... -} ... -} - D adota os comentários
//e/* ... */de C e ainda introduz uma sintaxe separada de comentário recursivo no formato/+ ... +/ - A documentação da gramática lexical de D é formal e detalhada, oferecendo bem as informações necessárias para implementação
- O documento de sintaxe lexical de D trata de detalhes como strings hexadecimais e strings heredoc
- D tem várias formas de string, como
\"...\", strings com crase,r\"...\",q\"...\",x\"...\"etc.
Tcl e JavaScript: aspas e terminadores de linha invisíveis
- Em Tcl, aspas podem aparecer dentro de identificadores
puts a\"bimprimea\"b- Também é possível colocar aspas em nomes de variáveis, mas ao referenciá-las deve-se usar
${a\"b}em vez de$a\"b
- JavaScript tem uma gramática lexical embutida para expressões regulares
- Em algo como
/[/]/g, o/dentro dos colchetes do conjunto de caracteres não precisa ser escapado - Um método que só procura a próxima barra de fechamento pode falhar em código minificado
- Em algo como
- ECMAScript define
u2028LINE SEPARATOR eu2029PARAGRAPH SEPARATOR como terminadores de linha- Na prática, esses caracteres se comportam como
\\n - Como são caracteres do tipo Trojan Source, no Emacs eles são configurados para aparecer como
↵e¶ - Muitos softwares não reconhecem esses caracteres e acabam renderizando-os como pontos de interrogação
- Fora D, o autor diz não conhecer outra linguagem com esse tipo de tratamento
- Na prática, esses caracteres se comportam como
- Essa característica permitiu criar um polyglot em C e JavaScript no SectorLISP
- lisp.js roda no navegador e também pode ser compilado com GCC para rodar localmente
- O llamafile consegue realçar corretamente esse tipo de código, e o autor diz não ter encontrado o mesmo comportamento em outros realçadores
Shell e os casos de borda do heredoc
- O heredoc de Shell permite escrever strings multilinha no formato
cat <<EOF ... EOF - A sintaxe de heredoc com aspas, como
cat <<'END', desativa a substituição de variáveis - Se o marcador do heredoc for uma string vazia, ele termina na próxima linha em branco
- O programa de exemplo imprime
helloeworldem duas linhas
- O programa de exemplo imprime
- Em linguagens com suporte a heredoc, como Shell, Ruby e Perl, é possível ter vários heredocs na mesma linha
- Em Shell, assim como em Tcl,
#nem sempre inicia um comentário- Em
${x#hi-},#é usado para remover um prefixo, e o exemplo imprimethere
- Em
Interpolação de strings: linguagens que exigem pilha de estados
- Em Kotlin, mesmo que a string comece com
\", a interpolação faz com que ao encontrar{seja necessário entrar em outro estado - TypeScript, Swift, Kotlin e Scala oferecem forte suporte a interpolação de strings no sentido de permitir código real dentro da string
- Para realçar Kotlin, Scala e TypeScript, é preciso contar chaves e manter uma pilha de estados do parser
- TypeScript é relativamente simples, então basta adicionar alguns estados à máquina de estados finitos
- Kotlin e Scala oferecem interpolação tanto com aspas duplas quanto com aspas triplas, por isso só o lexing de strings exigiu cerca de 13 estados independentes
- Swift suporta a sintaxe de interpolação
\"\\(var)\"e aspas triplas, mas a implementação ainda precisou de 10 estados
Swift, C# e FORTH: diferenças na forma de delimitar strings
- Swift aceita a sintaxe de strings
\"...\",\"\"\"...\"\"\"e/regex/delimitadas por uma quantidade arbitrária de#- O número de
#dos dois lados precisa coincidir - Isso resolve o problema de colocar aspas ou delimitadores de regex dentro da string
- O número de
- C# é parecido com as strings multilinha de aspas triplas do Python, mas permite usar no início e no fim uma quantidade ainda maior de aspas, desde que coincidam
- A quantidade de aspas à esquerda define a condição de término à direita
- O autor considera que permitir uma quantidade arbitrária de aspas reduz as regras de validade em comparação com a string clássica de aspas triplas do Python, tornando a decodificação mais simples com uma máquina de estados finitos
- FORTH tokeniza tudo com base em separação por espaços
- A sintaxe de início de string
c\"também é um único token c\" hello world\"tem o mesmo significado que\"hello world\"em outras linguagens
- A sintaxe de início de string
FORTRAN e COBOL: regras de colunas fixas
- O llamafile é apresentado como um caso de uso útil para ajudar na manutenção de sistemas bancários mesmo depois da aposentadoria de programadores de FORTRAN e COBOL
- Segundo o texto, é possível pedir código COBOL e FORTRAN a uma IA controlável em ambiente air-gapped, como Gemma 27b
- As regras de colunas fixas de FORTRAN são as seguintes
- Se houver
*,couCna coluna 1, a linha vira comentário - Se houver um caractere não branco na coluna 6, é possível continuar uma linha que ultrapassa 80 caracteres
- Se houver números nas colunas 1 a 5, eles formam um rótulo
- Se houver
- As regras de COBOL são as seguintes
- Se houver
*na coluna 7, a linha vira comentário - Se houver
-na coluna 7, é possível continuar uma linha que ultrapassa 80 caracteres - As colunas 1 a 6 contêm o número da linha
- Se houver
Zig e Lua: soluções diferentes para strings multilinha
- Zig tem uma sintaxe de string multilinha iniciada por duas barras invertidas
- Isso elimina a necessidade de chamar
textwrap.dedent()em strings com aspas triplas ao estilo Python - O autor considera que a desvantagem é o ponto e vírgula ficar pouco elegante
- A sintaxe é sugerida como algo que linguagens sem ponto e vírgula obrigatório, como Go, Scala e Python, poderiam considerar
- Isso elimina a necessidade de chamar
- A string multilinha de Lua se baseia em
[[...]]e permite inserir uma quantidade arbitrária de=entre os colchetes- Em algo como
[==[ ... ]==], a quantidade de=no início e no fim precisa coincidir - O mesmo esquema também pode ser usado em comentários
- São possíveis tanto
--[[ ... ]]quanto--[==[ ... ]==]
- Em algo como
Assembly: combinação de dialetos e pré-processadores
- Assembly é uma das linguagens mais difíceis de realçar porque existem muitos dialetos diferentes
- O llamafile tenta lidar de forma razoável com várias sintaxes, como AT&T e nasm
- As palavras-chave podem ser tratadas como o primeiro identificador da linha que não venha depois de dois-pontos, o que já faz a maior parte dos códigos assembly parecer aceitável
- A sintaxe de comentários também não é simples
- O comentário original do UNIX exigia apenas uma única
/ - O GNU as ainda oferece suporte a isso, mas só no início da linha
- Segundo o texto, o Clang não oferece suporte ao fixed comment, o que dificulta seu uso prático em código open source
- O comentário original do UNIX exigia apenas uma única
- O assembler original do UNIX não usava aspas de fechamento em literais de caractere
'xrepresenta o valor0x78do caracterex- O GNU as ainda suporta isso, mas o LLVM não
- Como esse formato aparece em código legado, um bom realçador deve suportá-lo
- O GNU assembler permite identificadores entre aspas, de modo que quase qualquer caractere pode entrar em um símbolo
- Assembly costuma ser usado junto com o pré-processador de C ou com m4
- Linhas iniciadas com
dnl,m4_dnlouCtambém devem ser tratadas como comentário
- Linhas iniciadas com
Ada e BASIC: casos em que uma gramática pequena bagunça o lexing
- Ada é relativamente simples de fazer lexing, mas o uso de aspas simples é peculiar
- Assim como em C, pode haver literais de caractere como
'x' - Aspa simples também é usada para referenciar atributos, como em
Foo'Size Character'(')')'Imagedeclara um caractere e o converte em representação textual por meio da funçãoImage
- Assim como em C, pode haver literais de caractere como
- O exemplo de Commodore BASIC quebra várias suposições comuns de realce de sintaxe
- Strings podem omitir a aspa de fechamento no fim da linha
- Nomes de variáveis podem ter sigils como
$ - Palavras-chave como
gotosão analisadas agressivamente até mesmo dentro de identificadores
- Visual BASIC tem sintaxe de literal de data como
#1/1/2024# - Visual BASIC também tem diretivas de pré-processador como
#If DEBUG Then,#Elsee#End If, o que dificulta o lexing
Perl: a complexidade entre Shell e linguagem de programação
- Perl fica entre Shell e linguagem de programação, herdando a complexidade dos dois lados
- Tornou expressões regulares um elemento de primeira classe da linguagem, e sua influência chegou a outras linguagens como Python
- A sintaxe de substituição do Perl lembra a do sed, como em
s/hello/Perl/i- Em vez de
/, pode-se usar qualquer caractere de pontuação como delimitador - Formas como
s!hello!Perl!isão úteis quando há barras dentro da regex - Com caracteres pareados, como em
s{hello}{Perl}i, é necessário um caractere adicional
- Em vez de
- Perl tem muitos prefixos mágicos que também precisam ser realçados como strings
m,s,y,qr,qw,qq,qxetc. aparecem com vários delimitadores diferentes
- Para não interpretar
y/x/y/como divisão, é preciso considerar o contexto- Como variáveis de Perl usam sigils como escalar
$, array@e hash%, isso ajuda na distinção mesmo sem parsing completo da gramática
- Como variáveis de Perl usam sigils como escalar
- Em Perl, é comum embutir documentação POD de páginas man no código-fonte
- Um
=wordno início da linha inicia a documentação POD e=cuta encerra
- Um
Ruby: o alvo de lexing mais difícil
- Ruby parece uma união de várias das linguagens anteriores, e sua gramática é avaliada como insuficientemente documentada de forma formal
- O documento de syntax do manual Ruby é visto como leve nos detalhes
- Ruby suporta a sintaxe com backquote e ao mesmo tempo permite usar backquote como se fosse nome de método, o que dificulta para o realçador decidir se aquilo é string ou não
- Ruby tem tanto o operador
<<quanto heredoc- Em código real, existem formas como
options[:includes] <<arg; true - Isso pode parecer heredoc, e até o Emacs interpreta errado
- Em código real, existem formas como
- Também é válido código como
puts \"This is #{<<HERE.strip} evil\", em que há um heredoc dentro da interpolação de string - Entre as 42 linguagens, Ruby foi considerada a maior surpresa; o autor sugere que Ruby talvez não possa ser analisada lexicalmente sem parsing, e que mesmo com parsing pode ser difícil ter certeza de como interpretá-la
Complexidade medida em linhas de código de implementação
- Considerando a quantidade de linhas de código de cada implementação de realçador, FORTH foi a mais simples e Ruby a mais complexa
- A implementação mais curta é highlight_forth.cpp, com 125 linhas
- Entre as implementações relativamente curtas estão m4 com 132 linhas, Ada com 149, LISP com 160, MATLAB com 166, COBOL com 186, BASIC com 199 e FORTRAN com 200
- Entre as implementações de tamanho intermediário estão JavaScript com 337 linhas, TypeScript com 371, Kotlin com 387, Scala com 387, Assembly com 447, C com 449, Swift com 455 e D com 521
- As implementações mais longas são Shell com 570 linhas, Perl com 583 e highlight_ruby.cpp com 1042 linhas
1 comentários
Opiniões do Hacker News
Meu trigraph favorito de C é algo como
do_action() ??!??! handle_error()Parece uma sintaxe especial de tratamento de erros, mas, quando você entende,
??!??!vira o OR lógico||e, pelas regras de avaliação de curto-circuito,handle_error()é executado quandodo_action()retorna um valor diferente de 0, o que é bem satisfatório??!é convertido em|, então??!??!vira||, ou seja, “or”||por razões estéticas?Foi uma leitura divertida, mas acabei me identificando mais com a perspectiva do Lisp
Pelo que entendi, a posição é que a sintaxe não é uma parte tão importante de uma linguagem e tende a virar mais um obstáculo do que uma ajuda; por isso ela deveria ser o mais simples e uniforme possível, para que se possa focar em outras coisas
Dito isso, aprender edição estrutural em Lisp até agora tem sido mais um obstáculo do que uma ajuda, e imagino que um dia a recompensa venha
Torná-la simples como em Lisp só empurra a discussão sintática para questões semânticas; é apenas uma mudança de camada
Acho uma sintaxe complexa muito mais fácil de ler e escrever do que uma sintaxe simples com semântica complexa. Erros de sintaxe dão feedback rápido, mas erros semânticos podem ficar escondidos até o momento da execução
Por exemplo, há algo como https://pyret.org/. Na prática, é difícil dizer que Lisp seja simples ou necessariamente uniforme
parinfere saber apenas os atalhos dos três comandosslurp,barferaiseSó com esses quatro itens já dá para aproveitar cerca de 95% das vantagens do paredit sem a complexidade, e o resto dos truques pode ser aprendido depois que você se acostumar
Havia uma parte dizendo “não sei quem quer fazer syntax highlighting de C a 35 MB por segundo, mas agora é possível”; é rápido, mas o
tcccompila 29 MB por segundo de C para código binário mesmo em computadores muito antigos: https://bellard.org/tcc/#speedProvavelmente dá para fazer ainda mais rápido, mas talvez nem seja necessário
Há algumas coisas que o autor deixou passar
Não foram só TypeScript, Swift, Kotlin e Scala que chegaram ao extremo de colocar código real em interpolação de strings; C#, Python, JavaScript, Ruby, Shell e Make também oferecem algo parecido
Em Tcl, a distinção entre código e dados é difícil, então
{ }é, na prática, um delimitador de string peculiar, e emxyzzy {#hello world}não dá para saber antes da avaliação se#hello worldé um comentário ou uma stringPostgreSQL tem as convenientes strings com aspas de dólar, de modo que, como em https://www.postgresql.org/docs/current/sql-syntax-lexical.h...,
'Dianne''s horse',$$Dianne's horse$$e$SomeTag$Dianne's horse$SomeTag$têm o mesmo significadoUma string como
"I have $foo $bar's: @{[$bar x $foo]}"pode imprimirI have 5 x's: xxxxxA sintaxe
@{[...]}explora uma característica do Perl que permite interpolação de arrays, não só de escalares; o[...]interno cria uma referência de array, e o@{...}externo a desreferencia. O interpretador Perl permite código arbitrário nessa expressão internaAssim, seria possível aninhar como em
"foo { toUpper("bar { x + y } bar") } foo", e, se+for concatenação de strings, isso seria essencialmente igual a"foo " + toUpper("bar " + (x + y) + " bar") + " foo"Não sei se existe alguma linguagem que realmente funcione assim
Eu gosto de Ruby, mas é difícil defender Ruby válido como
puts "This is #{<, que mistura interpolação de strings e heredocAlém disso, em Ruby espaço em branco também pode ser caractere de aspas. Em um contexto sem operando à esquerda,
%inicia uma string entre aspas, e o próximo caractere indica o tipo de aspas; portanto% hellovira uma string contendohello.%(this is a string)ou%{this is a string}são bons, mas nunca vi uso de espaço em branco na prática e oirbtambém não lida direito com isso, então eu não me importaria se fosse removidoSe você escrever
val s"${a} + ${b}" = "1 + 2",avira1ebvira2Antigamente, em loops quentes, código como
log.trace($"Entering iteration {i} for customer {c.ID} [{c.ShortName}]");chamavastring.Concattoda vez, antes mesmo de o logger sair, então muitos loggers implementavam sua própria interpolaçãoEm C#, é possível declarar uma sobrecarga que receba
DefaultInterpolatedStringHandlerou um padrão de handler customizado; essa sobrecarga tem prioridade e pode verificar se o log realmente é necessário antes de adiar a criação da stringHá outra peculiaridade sintática não mencionada aqui que quebra a maioria dos realçadores de sintaxe
Em Java, escapes Unicode podem aparecer não só dentro de strings, mas em qualquer lugar
Por exemplo,
class Foo\u007b}é uma classe válida, e um\u000Adentro de um comentário//pode ser tratado como uma quebra de linha real, então o exemplo comasserttambém se comporta de forma diferente do esperadoComo é possível encerrar um comentário de bloco com um escape Unicode, para esconder código malicioso em um comentário dentro de um arquivo-fonte Java basta criar uma desculpa para haver um bloco de escapes Unicode dentro do comentário
Um desenvolvedor que não conheça esse recurso provavelmente vai achar que está comentado e passar batido
Há um trecho dizendo que “Ruby é uma linguagem que escapa de todas as tentativas de entender sua sintaxe”, mas, do ponto de vista do TeX, que tem um lexer reprogramável arbitrariamente, isso é até bonitinho
Dizem que “todo programador C sabe que não dá para colocar um comentário de várias linhas dentro de outro comentário de várias linhas”, mas, para programadores de Standard ML, isso pode ser uma limitação surpreendente
Comentários aninhados como
(* (* Nested (**) *) comment *)são válidos, e umval _ = print "hello, world\n"em seguida também executa normalmenteConsiderando que C era visto como uma linguagem expressiva quando surgiu, é curioso que comentários aninhados não tenham entrado na linguagem
ML não tinha comentários de uma linha, então isso também é uma limitação igualmente surpreendente; chamar C de “expressiva” é algo que nunca ouvi, mas talvez fosse verdade em 1972 se comparada a assembly
E não sei que relação a sintaxe de comentários tem com a expressividade da linguagem; por definição, diria que nenhuma
Tradicionalmente, a análise léxica era vista como uma área que usa apenas autômatos de estado finito, como expressões regulares, então esse tipo de recurso parecia estar fora do escopo do lexing
#if 0Dá para envolver algo como
#if 0 ... #if 0 ... #endif ... #endifGostaria de ver a implementação de realce de sintaxe do joe em um formato reutilizável
O formato de https://joe-editor.sf.net/ é poderoso o suficiente para realçar corretamente f-strings do Python
A documentação e exemplos relacionados estão em https://github.com/cmur2/joe-syntax/blob/joe-4.4/misc/HowItW..., https://gist.github.com/irdc/6188f11b1e699d615ce2520f03f1d0d...
A gramática de estados e transições era muito mais fácil de entender do que a das ferramentas padrão
A desvantagem é que o conjunto de regras fica verboso e uma estruturação ideal se torna um pouco difícil, mas vejo isso até como uma vantagem no sentido de que fica mais fácil inferir todas as regras de geração a partir do código
O autor parece não ter tentado realçar sintaxe de TeX
Talvez isso seja bom para a saúde mental, porque, em TeX, normalmente é impossível fazer realce de sintaxe completo sem interpretar
Nem só fazer parsing basta, pois é possível redefinir o papel de cada caractere, permitindo coisas como “agora K é
{e C é}”De fato, há artigos no arXiv que usam esse recurso amaldiçoado
.texque encontrei no meu disco rígidoO objetivo é cobrir 99,9% do uso no mundo real. Assim, é provável que também cubra em geral o que um LLM produziria
Sintaxes obscuras normalmente não são um grande problema, desde que não façam uma string ou comentário continuar até o fim do arquivo e engolir o restante do código-fonte
\makeatletternão significa “transformar algo em um caractere at”, mas sim tratar o caractere@como letra durante o parsingAcho que não é fácil escrever um bom mecanismo de coloração de sintaxe como o do Vim
A coloração de sintaxe exige processamento de contexto, aplicando regras diferentes a conteúdos aninhados de determinadas formas
O realçador de sintaxe do Vim permite declarar dois tipos de itens,
matcheregion:matché uma regra léxica simples, enquantoregiontem expressões para casar início, fim e partes intermediárias separadamenteUm item pode ser declarado para ficar ativo apenas quando estiver dentro de uma determinada região, e as relações de inclusão também podem ser declaradas mutuamente
Sobre essa estrutura semântica básica, há vários recursos acrescentados para casos especiais. Mesmo para a Justine, seria difícil criar isso na hora em uma entrevista; acho que seria mais algo para uma tarefa de uma noite
Há aqui um script
genmanque recebe o resultado da conversão de manpage para HTML e o refina para o HTML do manual do TXR: https://www.kylheku.com/cgit/txr/tree/genman.txrAs partes que aparecem em branco são templates literais, e o código Lisp fica dentro de diretivas como
@(do ...). Palavras-chave de TXR aparecem em roxo, palavras-chave de TXR Lisp em verde, e até a mesma palavra muda conforme o contextoQuasi-strings também podem conter sintaxe aninhada, e dentro delas pode haver novamente código embutido ou outras quasi-strings. Os arquivos de definição de sintaxe
txr.vimetl.vimsão ambos gerados a partir de https://www.kylheku.com/cgit/txr/tree/genvim.txrSe não fazem assim, imagino que seja por questões de eficiência. Por exemplo, em vez de realçar só a parte visível na tela, seria preciso parsear o arquivo inteiro, o que poderia ficar caro