Arquitetura
Este documento abrange alguns dos elementos internos do Biome e como eles são usados dentro do projeto.
Parser e CST
Section titled “Parser e CST”A arquitetura do parser é impulsionada por um fork interno do rowan, uma biblioteca que implementa o padrão Green and Red tree.
O CST (Árvore de Sintaxe Concreta) é uma estrutura de dados muito semelhante à AST (Árvore de Sintaxe Abstrata) que mantém o registro de todas as informações de um programa, incluindo trivialidades.
Trivialidades são representadas por todas aquelas informações que são importantes para a execução de um programa:
- espaços
- tabs
- comentários
As trivialidades são anexadas a um nó. Um nó pode ter trivialidades antecedentes e subsequentes. Se você ler o código da esquerda para a direita, as trivialidades antecedentes aparecem antes de uma palavra-chave e as trivialidades subsequentes aparecem depois de uma palavra-chave.
As trivialidades antecedentes e subsequentes são categorizadas da seguinte forma:
- Toda trivialidade até o token/palavra-chave (incluindo quebras de linha) será a trivialidade antecedente;
- Tudo até a próxima quebra de linha (mas não incluindo-a) será a trivialidade subsequente;
Dado o seguinte trecho de JavaScript, // comentário 1 é uma trivialidade subsequente do token ;, e // comentário 2 é uma trivialidade antecedente da palavra-chave const. Abaixo uma versão minimizada do CST representado pelo Biome:
const a = "foo"; // comentário 1// comentário 2const b = "bar";0: JS_MODULE@0..55    ...      1: SEMICOLON@15..27 ";" [] [Whitespace(" "), Comments("// comment 1")]    1: JS_VARIABLE_STATEMENT@27..55        ...        1: CONST_KW@27..45 "const" [Newline("\n"), Comments("// comment 2"), Newline("\n")] [Whitespace(" ")]  3: EOF@55..55 "" [] []O CST nunca é diretamente acessível por design, um desenvolvedor pode ler suas informações usando a Árvore Vermelha, usando uma série de APIs que são geradas automaticamente a partir da gramática da linguagem.
Parser resiliente e recuperável
Section titled “Parser resiliente e recuperável”Para construir um CST, é necessário um parser resiliente e recuperável:
- resiliente: um parser capaz de retomar a análise após encontrar um erro de sintaxe pertencente à linguagem;
- recuperável: um parser capaz de entender onde ocorreu um erro e ser capaz de retomar a análise criando informações corretas;
A parte recuperável do parser não é uma ciência exata, e não há regras definitivas. Isso significa que, dependendo do que o parser estava analisando e onde ocorreu um erro, ele pode se recuperar de maneiras esperadas.
Para proteger os consumidores de consumir sintaxe incorreta, o parser também usa nós Bogus. Esses nós são usados para decorar o código quebrado causado por um erro de sintaxe.
No seguinte exemplo, os parênteses no while estão faltando, embora o parser seja capaz de se recuperar de maneira satisfatória, e ele consegue representar o código com um CST decente. Os parênteses e a condição do loop são marcados como ausentes, e o bloco de código é analisado corretamente:
while {}JsModule {  interpreter_token: missing (optional),  directives: JsDirectiveList [],  items: JsModuleItemList [    JsWhileStatement {      while_token: WHILE_KW@0..6 "while" [] [Whitespace(" ")],      l_paren_token: missing (required),      test: missing (required),      r_paren_token: missing (required),      body: JsBlockStatement {        l_curly_token: L_CURLY@6..7 "{" [] [],        statements: JsStatementList [],        r_curly_token: R_CURLY@7..8 "}" [] [],      },    },  ],  eof_token: EOF@8..8 "" [] [],}Este é o erro emitido durante a análise:
main.tsx:1:7 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  ✖ expected `(` but instead found `{`
  > 1 │ while {}      │       ^
  ℹ Remove {O mesmo não pode ser dito para o seguinte trecho. O parser não consegue entender corretamente a sintaxe durante a fase de recuperação, então ele precisa confiar nos nós de bogus para marcar alguma sintaxe como errônea. Note o JsBogusStatement:
function}JsModule {  interpreter_token: missing (optional),  directives: JsDirectiveList [],  items: JsModuleItemList [    TsDeclareFunctionDeclaration {      async_token: missing (optional),      function_token: FUNCTION_KW@0..8 "function" [] [],      id: missing (required),      type_parameters: missing (optional),      parameters: missing (required),      return_type_annotation: missing (optional),      semicolon_token: missing (optional),    },    JsBogusStatement {      items: [        R_CURLY@8..9 "}" [] [],      ],    },  ],  eof_token: EOF@9..9 "" [] [],}Este é o erro que obtemos da fase de análise:
main.tsx:1:9 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  ✖ expected a name for the function in a function declaration, but found none
  > 1 │ function}      │         ^Formatador (Em Progresso)
Section titled “Formatador (Em Progresso)”Lint (Em Progresso)
Section titled “Lint (Em Progresso)”Daemon (Em Progresso)
Section titled “Daemon (Em Progresso)”O Biome usa uma arquitetura de servidor-cliente para executar suas tarefas.
Um daemon é um servidor de longa duração que o Biome inicia em segundo plano e usa para processar solicitações do editor e da CLI.
Copyright (c) 2023-present Biome Developers and Contributors.