Tree Sitter Lua

2018 • LIBRARY

Uma gramática completa da linguagem Lua (compatível com a versão 5.4) para o sistema de parsing Tree-sitter. O projeto foi escrito em JavaScript (definição da gramática) e C (scanner externo), o scanner externo lida com regras complexas como comentários e strings.

A biblioteca é publicada e consumível em múltiplos ecossistemas, incluindo Node.js (NPM), Rust (Crates.io) e Swift (SPM).


🧩 Desafios Técnicos & Soluções

1. Gramática e Precedência de Operadores

O Problema: Definir a gramática em grammar.js para resolver conflitos de parsing LR(1) comuns em linguagens dinâmicas.

A Solução: Definição cuidadosa de precedência de operadores (ex: PREC.COMPARATIVE, PREC.UNARY) e a refatoração de regras complexas como binary_expression.

Resultado:

  • Eliminação de ambiguidades.
  • Parsing preciso.

2. Scanner Externo para Strings e Comentários

O Problema: A gramática declarativa do Tree-sitter não consegue lidar com strings e comentários longos do Lua (ex: [=[ ... ]=]), que podem ter um número variável de sinais de igual.

A Solução: Foi necessário implementar um scanner externo em C (scanner.c) que gerencia o estado (armazenando o nível (depth) dos delimitadores).

Resultado:

  • Parse correto de blocos de texto complexos.

3. Suporte à Sintaxe Lua 5.4

O Problema: Manter a gramática atualizada com as últimas especificações da linguagem.

A Solução: O projeto foi atualizado para dar suporte completo à especificação do Lua 5.4. Isso incluiu a adição de novas regras, como os atributos de variáveis locais (<const> e <close>) e literais hexadecimais complexos.

Resultado:

  • Compatibilidade total com a versão mais recente da linguagem.

4. Configuração de Build Multi-Linguagem

O Problema: Fornecer a biblioteca para múltiplos ecossistemas (Node.js, Rust, Swift) com sistemas de build distintos.

A Solução: Configurei pipelines de build específicos: binding.gyp para Node.js e Cargo.toml com build.rs para Rust. Uma contribuição de terceiros garantiu build para Swift (Package.swift).

Resultado:

  • Garante que os mesmos fontes C (parser.c e scanner.c) sejam compilados corretamente.
  • Biblioteca consumível nativamente em vários ambientes.

🏗️ Arquitetura

O núcleo da gramática é definido em JavaScript (grammar.js), que é compilado pelo Tree-sitter CLI para gerar o parser em C (parser.c). Um scanner externo (scanner.c) é escrito em C para lidar com regras de parsing que exigem estado.

Bindings Multi-linguagem

Os bindings são fornecidos nativamente para:

  • Node.js: Usando node-gyp e nan para compilar o wrapper em C++.
  • Rust: Usando cc no build.rs para compilar os fontes C e linkar com a crate Rust.
  • Swift: Usando o Swift Package Manager (Package.swift) para expor os cabeçalhos C.

🛠️ Tech Stack

  • Gramática: JavaScript
  • Parsing: C
  • Bindings: Node.js, Rust, Swift
  • CI: GitHub Actions