Simplify parsing of special LaTeX commands

pull/511/head
Patrick Förster 2021-12-27 13:22:53 +07:00
parent 2c0d03a36e
commit ccb6fe7a57
38 changed files with 137587 additions and 548313 deletions

@ -0,0 +1,5 @@
---
BasedOnStyle: LLVM
IndentWidth: 2
---

1
.gitattributes vendored

@ -1 +1,2 @@
/src/** linguist-vendored
/examples/* linguist-vendored

@ -0,0 +1,17 @@
version: 2
updates:
- package-ecosystem: cargo
directory: '/'
schedule:
interval: daily
open-pull-requests-limit: 10
- package-ecosystem: npm
directory: '/'
schedule:
interval: daily
open-pull-requests-limit: 10
- package-ecosystem: github-actions
directory: '/'
schedule:
interval: daily
open-pull-requests-limit: 10

@ -1,18 +1,53 @@
name: CI
on:
push:
branches: [master]
branches: [master, develop, feature/*]
pull_request:
branches: [master]
env:
CARGO_INCREMENTAL: 0
CI: 1
RUST_BACKTRACE: short
RUSTFLAGS: '-D warnings'
RUSTUP_MAX_RETRIES: 10
jobs:
build:
runs-on: ubuntu-latest
node:
name: Node
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
- name: Checkout Repository
uses: actions/checkout@v2
- name: Install Node.js
uses: actions/setup-node@v2
with:
node-version: '12'
- name: Install dependencies
run: npm ci
- name: Run tests
node-version: '16'
cache: 'npm'
- name: Install Dependencies
run: npm install
- name: Test
run: npm test
rust:
name: Rust
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Install Rust Toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Cache Dependencies
uses: Swatinem/rust-cache@v1
- name: Compile
run: cargo test --no-run
- name: Test
run: cargo test -- --nocapture --quiet

58
.gitignore vendored

@ -1,6 +1,5 @@
# Created by https://www.toptal.com/developers/gitignore/api/c,c++,node,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=c,c++,node,visualstudiocode
# Created by https://www.toptal.com/developers/gitignore/api/c,c++,node,visualstudiocode,rust
# Edit at https://www.toptal.com/developers/gitignore?templates=c,c++,node,visualstudiocode,rust
### C ###
# Prerequisites
@ -83,6 +82,7 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
@ -119,8 +119,8 @@ build/Release
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
@ -149,7 +149,7 @@ typings/
# dotenv environment variables file
.env
.env.test
.env*.local
.env.production
# parcel-bundler cache (https://parceljs.org/)
.cache
@ -157,6 +157,7 @@ typings/
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
@ -186,18 +187,57 @@ dist
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
### Node Patch ###
# Serverless Webpack directories
.webpack/
# Optional stylelint cache
.stylelintcache
# SvelteKit build / generate output
.svelte-kit
### Rust ###
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide
# End of https://www.toptal.com/developers/gitignore/api/c,c++,node,visualstudiocode
# Support for Project snippet scope
!.vscode/*.code-snippets
# End of https://www.toptal.com/developers/gitignore/api/c,c++,node,visualstudiocode,rust
build/
target/
Cargo.lock

@ -4,23 +4,25 @@ description = "latex grammar for the tree-sitter parsing library"
version = "0.0.1"
keywords = ["incremental", "parsing", "latex"]
categories = ["parsing", "text-editors"]
repository = "https://github.com/tree-sitter/tree-sitter-javascript"
repository = "https://github.com/tree-sitter/tree-sitter-latex"
edition = "2018"
license = "MIT"
build = "bindings/rust/build.rs"
include = [
"bindings/rust/*",
"grammar.js",
"queries/*",
"src/*",
"bindings/rust/*",
"grammar.js",
"queries/*",
"src/*",
"rules/*",
"util/*",
]
[lib]
path = "bindings/rust/lib.rs"
[dependencies]
tree-sitter = "0.19"
tree-sitter = "~0.20.1"
[build-dependencies]
cc = "1.0"

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2020 Patrick Förster
Copyright (c) 2021 Patrick Förster
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

@ -3,4 +3,4 @@
[![CI](https://github.com/latex-lsp/tree-sitter-latex/workflows/CI/badge.svg)](https://github.com/latex-lsp/tree-sitter-latex/actions)
LaTeX grammar for [tree-sitter](https://github.com/tree-sitter/tree-sitter).
This grammar may eventually find use in the [TexLab](https://github.com/latex-lsp/texlab) language server.
This grammar will find use in the [TexLab](https://github.com/latex-lsp/texlab) language server.

@ -1,19 +1,19 @@
{
"targets": [
{
"target_name": "tree_sitter_latex_binding",
"include_dirs": [
"<!(node -e \"require('nan')\")",
"src"
],
"sources": [
"bindings/node/binding.cc",
"src/parser.c",
# If your language uses an external scanner, add it here.
],
"cflags_c": [
"-std=c99",
]
}
]
"targets": [
{
"target_name": "tree_sitter_latex_binding",
"include_dirs": [
"<!(node -e \"require('nan')\")",
"src"
],
"sources": [
"bindings/node/binding.cc",
"src/parser.c",
"src/scanner.c"
],
"cflags_c": [
"-std=c99",
]
}
]
}

@ -13,13 +13,11 @@ fn main() {
// If your language uses an external scanner written in C,
// then include this block of code:
/*
let scanner_path = src_dir.join("scanner.c");
c_config.file(&scanner_path);
println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap());
*/
c_config.compile("parser");
c_config.compile("tree-sitter-latex");
println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap());
// If your language uses an external scanner written in C++,

@ -1,182 +0,0 @@
{
"citation": [
"\\cite",
"\\cite*",
"\\Cite",
"\\nocite",
"\\citet",
"\\citep",
"\\citet*",
"\\citep*",
"\\citeauthor",
"\\citeauthor*",
"\\Citeauthor",
"\\Citeauthor*",
"\\citetitle",
"\\citetitle*",
"\\citeyear",
"\\citeyear*",
"\\citedate",
"\\citedate*",
"\\citeurl",
"\\fullcite",
"\\citeyearpar",
"\\citealt",
"\\citealp",
"\\citetext",
"\\parencite",
"\\parencite*",
"\\Parencite",
"\\footcite",
"\\footfullcite",
"\\footcitetext",
"\\textcite",
"\\Textcite",
"\\smartcite",
"\\Smartcite",
"\\supercite",
"\\autocite",
"\\Autocite",
"\\autocite*",
"\\Autocite*",
"\\volcite",
"\\Volcite",
"\\pvolcite",
"\\Pvolcite",
"\\fvolcite",
"\\ftvolcite",
"\\svolcite",
"\\Svolcite",
"\\tvolcite",
"\\Tvolcite",
"\\avolcite",
"\\Avolcite",
"\\notecite",
"\\notecite",
"\\pnotecite",
"\\Pnotecite",
"\\fnotecite"
],
"labelReference": [
"\\ref",
"\\vref",
"\\Vref",
"\\autoref",
"\\pageref",
"\\cref",
"\\Cref",
"\\cref*",
"\\Cref*",
"\\namecref",
"\\nameCref",
"\\lcnamecref",
"\\namecrefs",
"\\nameCrefs",
"\\lcnamecrefs",
"\\labelcref",
"\\labelcpageref"
],
"labelRangeReference": [
"\\crefrange",
"\\crefrange",
"\\Crefrange",
"\\Crefrange",
"\\crefrange*",
"\\crefrange*",
"\\Crefrange*",
"\\Crefrange*"
],
"glossaryEntryReference": [
"\\gls",
"\\Gls",
"\\GLS",
"\\glspl",
"\\Glspl",
"\\GLSpl",
"\\glsdisp",
"\\glslink",
"\\glstext",
"\\Glstext",
"\\GLStext",
"\\glsfirst",
"\\Glsfirst",
"\\GLSfirst",
"\\glsplural",
"\\Glsplural",
"\\GLSplural",
"\\glsfirstplural",
"\\Glsfirstplural",
"\\GLSfirstplural",
"\\glsname",
"\\Glsname",
"\\GLSname",
"\\glssymbol",
"\\Glssymbol",
"\\glsdesc",
"\\Glsdesc",
"\\GLSdesc",
"\\glsuseri",
"\\Glsuseri",
"\\GLSuseri",
"\\glsuserii",
"\\Glsuserii",
"\\GLSuserii",
"\\glsuseriii",
"\\Glsuseriii",
"\\GLSuseriii",
"\\glsuseriv",
"\\Glsuseriv",
"\\GLSuseriv",
"\\glsuserv",
"\\Glsuserv",
"\\GLSuserv",
"\\glsuservi",
"\\Glsuservi",
"\\GLSuservi"
],
"acronymReference": [
"\\acrshort",
"\\Acrshort",
"\\ACRshort",
"\\acrshortpl",
"\\Acrshortpl",
"\\ACRshortpl",
"\\acrlong",
"\\Acrlong",
"\\ACRlong",
"\\acrlongpl",
"\\Acrlongpl",
"\\ACRlongpl",
"\\acrfull",
"\\Acrfull",
"\\ACRfull",
"\\acrfullpl",
"\\Acrfullpl",
"\\ACRfullpl",
"\\acs",
"\\Acs",
"\\acsp",
"\\Acsp",
"\\acl",
"\\Acl",
"\\aclp",
"\\Aclp",
"\\acf",
"\\Acf",
"\\acfp",
"\\Acfp",
"\\ac",
"\\Ac",
"\\acp",
"\\glsentrylong",
"\\Glsentrylong",
"\\glsentrylongpl",
"\\Glsentrylongpl",
"\\glsentryshort",
"\\Glsentryshort",
"\\glsentryshortpl",
"\\Glsentryshortpl",
"\\glsentryfullpl",
"\\Glsentryfullpl"
]
}

@ -1,655 +1,90 @@
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
const commands = require('./commands.json');
const sepBy1 = (rule, sep) => seq(rule, repeat(seq(sep, rule)));
const sepBy = (rule, sep) => optional(sepBy1(rule, sep));
const rules = require('./rules');
module.exports = grammar({
name: 'latex',
extras: $ => [$._whitespace, $.comment],
word: $ => $.generic_command_name,
extras: $ => [$._whitespace, $.line_comment],
externals: $ => [
$._generic_command_name,
$._begin_command_name,
$._end_command_name,
$._iffalse_command_name,
$._fi_command_name,
$._caption_command_name,
$._citation_command_name,
$._label_definition_command_name,
$._label_reference_command_name,
$._label_reference_range_command_name,
$._label_number_command_name,
$._new_command_definition_command_name,
$._old_command_definition_command_name,
$._let_command_definition_command_name,
$._environment_definition_command_name,
$._glossary_entry_definition_command_name,
$._glossary_entry_reference_command_name,
$._acronym_definition_command_name,
$._acronym_reference_command_name,
$._theorem_definition_command_name,
$._color_definition_command_name,
$._color_set_definition_command_name,
$._color_reference_command_name,
$._tikz_library_import_command_name,
$._package_include_command_name,
$._class_include_command_name,
$._latex_include_command_name,
$._biblatex_include_command_name,
$._bibtex_include_command_name,
$._graphics_include_command_name,
$._svg_include_command_name,
$._inkscape_include_command_name,
$._verbatim_include_command_name,
$._import_command_name,
$._escaped_lparen_command_name,
$._escaped_rparen_command_name,
$._escaped_lbrack_command_name,
$._escaped_rbrack_command_name,
$._escaped_lcurly_command_name,
$._escaped_rcurly_command_name,
$._part_command_name,
$._chapter_command_name,
$._section_command_name,
$._subsection_command_name,
$._subsubsection_command_name,
$._paragraph_command_name,
$._subparagraph_command_name,
$._enum_item_command_name,
$._verbatim_cmd_fi,
$._verbatim_env_comment,
$._verbatim_env_verbatim,
$._verbatim_env_listing,
$._verbatim_env_minted,
],
supertypes: $ => [$._include],
rules: {
document: $ => repeat($._content),
source_file: $ => repeat($._full_content),
//--- Trivia ---//
...rules,
_whitespace: $ => /\s+/,
_full_content: $ =>
choice($._section, $._basic_content, $._contextual_delimiter, '[', ']'),
comment: $ => /%[^\r\n]*/,
_basic_content: $ =>
choice(alias($._environment, $.environment), $._minimal_content),
//--- Content ---//
_simple_content: $ =>
_minimal_content: $ =>
choice(
$.brace_group,
$.mixed_group,
$.param,
$.text,
$.curly_group,
$.block_comment,
$.displayed_equation,
$.inline_formula,
$.environment,
$.caption,
$.citation,
$.package_include,
$.class_include,
$.latex_include,
$.latex_input,
$.biblatex_include,
$.bibtex_include,
$.graphics_include,
$.svg_include,
$.inkscape_include,
$.verbatim_include,
$.import,
$.label_definition,
$.label_reference,
$.equation_label_reference,
$.label_reference_range,
$.label_number,
$.command_definition,
$.math_operator,
$.glossary_entry_definition,
$.glossary_entry_reference,
$.acronym_definition,
$.acronym_reference,
$.theorem_definition,
$.color_reference,
$.color_definition,
$.color_set_definition,
$.pgf_library_import,
$.tikz_library_import,
$.generic_command
),
_content: $ =>
choice(
$.part,
$.chapter,
$.section,
$.subsection,
$.subsubsection,
$.paragraph,
$.subparagraph,
$.enum_item,
$._simple_content
),
//--- Structure ---//
part: $ =>
prec.right(
seq(
field('command', choice('\\part', '\\part*')),
field('text', $.brace_group),
field(
'child',
repeat(
choice(
$.chapter,
$.section,
$.subsection,
$.subsubsection,
$.paragraph,
$.subparagraph,
$.enum_item,
$._simple_content
)
)
)
)
),
chapter: $ =>
prec.right(
seq(
field('command', choice('\\chapter', '\\chapter*')),
field('text', $.brace_group),
field(
'child',
repeat(
choice(
$.section,
$.subsection,
$.subsubsection,
$.paragraph,
$.subparagraph,
$.enum_item,
$._simple_content
)
)
)
)
),
section: $ =>
prec.right(
seq(
field('command', choice('\\section', '\\section*')),
field('text', $.brace_group),
field(
'child',
repeat(
choice(
$.subsection,
$.subsubsection,
$.paragraph,
$.subparagraph,
$.enum_item,
$._simple_content
)
)
)
)
),
subsection: $ =>
prec.right(
seq(
field('command', choice('\\subsection', '\\subsection*')),
field('text', $.brace_group),
field(
'child',
repeat(
choice(
$.subsubsection,
$.paragraph,
$.subparagraph,
$.enum_item,
$._simple_content
)
)
)
)
),
subsubsection: $ =>
prec.right(
seq(
field('command', choice('\\subsubsection', '\\subsubsection*')),
field('text', $.brace_group),
field(
'child',
repeat(
choice(
$.paragraph,
$.subparagraph,
$.enum_item,
$._simple_content
)
)
)
)
),
paragraph: $ =>
prec.right(
seq(
field('command', choice('\\paragraph', '\\paragraph*')),
field('text', $.brace_group),
field(
'child',
repeat(choice($.subparagraph, $.enum_item, $._simple_content))
)
)
),
subparagraph: $ =>
prec.right(
seq(
field('command', choice('\\subparagraph', '\\subparagraph*')),
field('text', $.brace_group),
field('child', repeat(choice($.enum_item, $._simple_content)))
)
),
enum_item: $ =>
prec.right(
seq(
field('command', '\\item'),
optional(seq('[', field('label', $.word), ']')),
field('child', repeat(choice($._simple_content)))
)
),
//--- Groups ---//
brace_group: $ =>
seq(
field('left', '{'),
field('child', repeat($._content)),
field('right', '}')
),
bracket_group: $ =>
seq(
field('left', '['),
field('child', repeat($._content)),
field('right', ']')
),
paren_group: $ =>
seq(
field('left', '('),
field('child', repeat($._content)),
field('right', ')')
),
mixed_group: $ =>
seq(
field('left', choice('(', '[', '\\{')),
field('child', repeat($._content)),
field('right', choice(')', ']', '\\}'))
),
key_val_options: $ =>
seq(
field('left', '['),
sepBy(field('property', $.key_val_pair), ','),
field('right', ']')
),
key_val_pair: $ =>
seq(
field('key', repeat1($.word)),
optional(seq(field('equals', '='), field('value', $._content)))
),
//--- Text ---//
// Performance optimization: store text as a binary tree instead of a list
// text: $ =>
// prec.right(
// seq(field('left', $._text_fragment), field('right', optional($.text)))
// ),
text: $ => prec.right(repeat1($._text_fragment)),
_text_fragment: $ => prec.right(choice($.word, ',', '=')),
word: $ => /[^\s\\%\{\},\$\[\]\(\)=\#]+/,
path: $ => /[^\*\"\[\]:;\|\{\}<>]+/,
param: $ => /#\d/,
//--- Math ---//
displayed_equation: $ =>
prec.left(
seq(
field('left', choice('$$', '\\[')),
field('child', repeat($._content)),
field('right', choice('$$', '\\]'))
)
),
inline_formula: $ =>
prec.left(
seq(
field('left', choice('$', '\\(')),
field('child', repeat($._content)),
field('right', choice('$', '\\)'))
)
),
//--- Environment ---//
begin: $ =>
prec.right(
seq(
field('command', '\\begin'),
'{',
field('name', $.word),
'}',
field('option', repeat($.bracket_group))
)
),
end: $ => seq(field('command', '\\end'), '{', field('name', $.word), '}'),
environment: $ =>
prec.right(
seq(
field('begin', $.begin),
field('child', repeat($._content)),
field('end', $.end)
)
),
//--- Special Commands ---//
caption: $ =>
seq(
field('command', '\\caption'),
field('short', optional($.bracket_group)),
field('long', $.brace_group)
),
citation: $ =>
seq(
field('command', choice(...commands.citation)),
optional(
seq(
field('prenote', $.bracket_group),
field('postnote', optional($.bracket_group))
)
),
'{',
sepBy(field('key', $.word), ','),
'}'
),
package_include: $ =>
seq(
field('command', choice('\\usepackage', '\\RequirePackage')),
field('option', optional($.key_val_options)),
'{',
sepBy(field('path', $.path), ','),
'}'
),
class_include: $ =>
seq(
field('command', '\\documentclass'),
field('option', optional($.key_val_options)),
'{',
sepBy(field('path', $.path), ','),
'}'
),
latex_include: $ =>
seq(
field('command', choice('\\include', '\\subfileinclude')),
'{',
sepBy(field('path', $.path), ','),
'}'
),
latex_input: $ =>
seq(
field('command', choice('\\input', '\\subfile')),
'{',
sepBy(field('path', $.path), ','),
'}'
),
biblatex_include: $ =>
seq(
field('command', '\\addbibresource'),
field('option', optional($.key_val_options)),
'{',
field('path', $.path),
'}'
),
bibtex_include: $ =>
seq(
field('command', '\\bibliography'),
'{',
sepBy(field('path', $.path), ','),
'}'
),
graphics_include: $ =>
seq(
field('command', '\\includegraphics'),
field('option', optional($.key_val_options)),
'{',
sepBy(field('path', $.path), ','),
'}'
),
svg_include: $ =>
seq(
field('command', '\\includesvg'),
field('option', optional($.key_val_options)),
'{',
sepBy(field('path', $.path), ','),
'}'
),
inkscape_include: $ =>
seq(
field('command', '\\includeinkscape'),
field('option', optional($.key_val_options)),
'{',
sepBy(field('path', $.path), ','),
'}'
),
verbatim_include: $ =>
seq(
field('command', choice('\\verbatiminput', '\\VerbatimInput')),
'{',
sepBy(field('path', $.path), ','),
'}'
),
import: $ =>
seq(
field(
'command',
choice(
'\\import',
'\\subimport',
'\\inputfrom',
'\\subimportfrom',
'\\includefrom',
'\\subincludefrom'
)
),
'{',
field('directory', $.path),
'}',
'{',
field('file', $.path),
'}'
),
label_definition: $ =>
seq(field('command', '\\label'), '{', field('name', $.word), '}'),
label_reference: $ =>
seq(
field('command', choice(...commands.labelReference)),
'{',
sepBy(field('label', $.word), ','),
'}'
),
equation_label_reference: $ =>
seq(
field('command', '\\eqref'),
'{',
sepBy1(field('label', $.word), ','),
'}'
),
label_reference_range: $ =>
prec.right(
seq(
field('command', choice(...commands.labelRangeReference)),
'{',
field('label1', $.word),
'}',
// optional to improve error handling
optional(seq('{', field('label2', $.word), '}'))
)
),
label_number: $ =>
seq(
field('command', '\\newlabel'),
'{',
field('label', $.word),
'}',
field('number', $.brace_group)
),
command_definition: $ =>
seq(
field(
'command',
choice('\\newcommand', '\\renewcommand', '\\DeclareRobustCommand')
),
field('argc', optional(seq('[', /\d/, ']'))),
'{',
field('name', $.generic_command_name),
'}',
field('implementation', $.brace_group)
),
math_operator: $ =>
seq(
field(
'command',
choice('\\DeclareMathOperator', '\\DeclareMathOperator*')
),
'{',
field('name', $.generic_command_name),
'}',
field('implementation', $.brace_group)
),
glossary_entry_definition: $ =>
seq(
field('command', '\\newglossaryentry'),
'{',
field('name', $.word),
'}',
'{',
sepBy(field('property', $.key_val_pair), ','),
'}'
),
glossary_entry_reference: $ =>
seq(
field('command', choice(...commands.glossaryEntryReference)),
field('option', optional($.key_val_options)),
'{',
field('name', $.word),
'}'
),
acronym_definition: $ =>
seq(
'\\newacronym',
field('option', optional($.key_val_options)),
'{',
field('name', $.word),
'}',
field('short', $.brace_group),
field('long', $.brace_group)
),
acronym_reference: $ =>
seq(
field('command', choice(...commands.acronymReference)),
field('option', optional($.key_val_options)),
'{',
field('name', $.word),
'}'
),
theorem_definition: $ =>
prec.right(
seq(
field('command', choice('\\newtheorem', '\\declaretheorem')),
'{',
field('name', $.word),
'}',
choice(
seq(
field('title', $.brace_group),
optional(seq('[', field('counter', $.word), ']'))
),
seq(
'[',
field('counter', $.word),
']',
field('title', $.brace_group)
)
)
)
),
color_reference: $ =>
prec.right(
seq(
field(
'command',
choice('\\color', '\\colorbox', '\\textcolor', '\\pagecolor')
),
'{',
field('name', $.word),
'}'
)
),
color_definition: $ =>
prec.right(
seq(
field('command', '\\definecolor'),
'{',
field('name', $.word),
'}',
'{',
field('model', $.word),
'}',
// optional to improve error handling
optional(seq('{', field('spec', $.text), '}'))
)
),
color_set_definition: $ =>
prec.right(
seq(
field('command', '\\definecolorset'),
optional(seq('[', field('ty', $.word), ']')),
'{',
sepBy(field('model', $.word), ','),
'}',
optional(
// optional to improve error handling
seq(
field('head', $.brace_group),
field('tail', $.brace_group),
field('spec', $.brace_group)
)
)
)
),
pgf_library_import: $ =>
seq(
field('command', '\\usepgflibrary'),
'{',
sepBy(field('name', $.word), ','),
'}'
),
tikz_library_import: $ =>
seq(
field('command', '\\usetikzlibrary'),
'{',
sepBy(field('name', $.word), ','),
'}'
),
//--- Generic commands ---//
generic_command: $ =>
prec.right(
seq(
field('name', $.generic_command_name),
field(
'arg',
repeat(choice($.brace_group, $.mixed_group))
)
)
$.math_set,
$.text,
$.macro_parameter,
$._command
),
generic_command_name: $ => /\\([^\r\n]|[@a-zA-Z]+\*?)?/,
_contextual_delimiter: $ => choice(',', '=', '(', ')'),
},
});

61
package-lock.json generated

@ -1,18 +1,65 @@
{
"name": "tree-sitter-latex",
"version": "0.1.0",
"lockfileVersion": 1,
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "tree-sitter-latex",
"version": "0.1.0",
"license": "MIT",
"dependencies": {
"nan": "^2.15.0"
},
"devDependencies": {
"prettier": "^2.5.1",
"tree-sitter-cli": "0.20.1"
}
},
"node_modules/nan": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
},
"node_modules/prettier": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"dev": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/tree-sitter-cli": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.20.1.tgz",
"integrity": "sha512-I0Gp4ThRp39TDnBAaZKiogvoE85MSeL6/ILZMXbzeEo8hUsudpVhEHRE4CU+Bk5QUaiMiDkD+ZIL3gT2zZ++wg==",
"dev": true,
"hasInstallScript": true,
"bin": {
"tree-sitter": "cli.js"
}
}
},
"dependencies": {
"nan": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
"integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ=="
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
},
"prettier": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
"integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
"dev": true
},
"tree-sitter-cli": {
"version": "0.19.4",
"resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.19.4.tgz",
"integrity": "sha512-p2kxjuoQeauXBu5eE+j7c5BMCRXmc17EiAswnnWn3ieUlHXBkA0Z7vRnaCSElDR34MhZnSgqgzuuzQk0cDqCjw==",
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.20.1.tgz",
"integrity": "sha512-I0Gp4ThRp39TDnBAaZKiogvoE85MSeL6/ILZMXbzeEo8hUsudpVhEHRE4CU+Bk5QUaiMiDkD+ZIL3gT2zZ++wg==",
"dev": true
}
}

@ -1,15 +1,16 @@
{
"name": "tree-sitter-latex",
"version": "0.1.0",
"description": "Tree-sitter Grammar for LaTeX",
"description": "LaTeX grammar for the tree-sitter parsing library",
"main": "bindings/node",
"scripts": {
"build": "tree-sitter generate",
"test": "tree-sitter test"
"generate": "tree-sitter generate",
"test": "tree-sitter test",
"test:update": "tree-sitter test --update"
},
"repository": {
"type": "git",
"url": "git+https://github.com/latex-lsp/tree-sitter-latex.git"
"url": "git+https://github.com/latex-lsp/texlab.git"
},
"keywords": [
"tree-sitter",
@ -19,13 +20,14 @@
"author": "Patrick Förster <patrick.foerster@outlook.de>",
"license": "MIT",
"bugs": {
"url": "https://github.com/latex-lsp/tree-sitter-latex/issues"
"url": "https://github.com/latex-lsp/texlab/issues"
},
"homepage": "https://github.com/latex-lsp/tree-sitter-latex#readme",
"homepage": "https://github.com/latex-lsp/texlab#readme",
"dependencies": {
"nan": "^2.14.2"
"nan": "^2.15.0"
},
"devDependencies": {
"tree-sitter-cli": "0.19.4"
"prettier": "^2.5.1",
"tree-sitter-cli": "0.20.1"
}
}

@ -0,0 +1,191 @@
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
const { commandName } = require('../util');
module.exports = {
_command: $ =>
choice(
$._include,
$.caption,
$.citation,
$.label_definition,
$.label_reference,
$.label_reference_range,
$.label_number,
$.new_command_definition,
$.old_command_definition,
$.let_command_definition,
$.environment_definition,
$.glossary_entry_definition,
$.glossary_entry_reference,
$.acronym_definition,
$.acronym_reference,
$.theorem_definition,
$.color_definition,
$.color_set_definition,
$.color_reference,
$.tikz_library_import,
alias($._generic_command, $.command),
),
_generic_command: $ =>
prec.right(
seq(
field('command_name', alias($._generic_command_name, $.command_name)),
repeat(choice($.curly_group, $.mixed_group)),
),
),
caption: $ =>
seq(
commandName($, 'caption'),
field('list_entry', optional($.brack_group)),
field('heading', $.curly_group),
),
citation: $ =>
seq(
commandName($, 'citation'),
optional(
seq(
field('prenote', $.brack_group),
field('postnote', optional($.brack_group)),
),
),
$.curly_group_key_list,
),
label_definition: $ =>
seq(commandName($, 'label_definition'), field('name', $.curly_group_key)),
label_reference: $ =>
seq(
commandName($, 'label_reference'),
field('names', $.curly_group_key_list),
),
label_reference_range: $ =>
seq(
commandName($, 'label_reference_range'),
field('name_lo', $.curly_group_key),
field('name_hi', $.curly_group_key),
),
label_number: $ =>
seq(
commandName($, 'label_number'),
field('name', $.curly_group_key),
field('number', $.curly_group),
),
new_command_definition: $ =>
seq(
commandName($, 'new_command_definition'),
field('declaration', $.curly_group_command_name),
field('argc', optional($.brack_group_argc)),
field('implementation', $.curly_group),
),
old_command_definition: $ =>
seq(
commandName($, 'old_command_definition'),
field('declaration', alias($._generic_command_name, $.command_name)),
),
let_command_definition: $ =>
seq(
commandName($, 'let_command_definition'),
field('declaration', alias($._generic_command_name, $.command_name)),
optional('='),
field('implementation', alias($._generic_command_name, $.command_name)),
),
environment_definition: $ =>
seq(
commandName($, 'environment_definition'),
field('name', $.curly_group_key),
field('argc', optional($.brack_group_argc)),
field('begin', $.curly_group_impl),
field('end', $.curly_group_impl),
),
glossary_entry_definition: $ =>
seq(
commandName($, 'glossary_entry_definition'),
field('name', $.curly_group_key),
field('properties', $.curly_group_key_value),
),
glossary_entry_reference: $ =>
seq(
commandName($, 'glossary_entry_reference'),
field('options', optional($.brack_group_key_value)),
field('name', $.curly_group_key),
),
acronym_definition: $ =>
seq(
commandName($, 'acronym_definition'),
field('options', optional($.brack_group_key_value)),
field('name', $.curly_group_key),
field('abbrv', $.curly_group),
field('full', $.curly_group),
),
acronym_reference: $ =>
seq(
commandName($, 'acronym_reference'),
field('options', optional($.brack_group_key_value)),
field('name', $.curly_group_key),
),
theorem_definition: $ =>
prec.right(
seq(
commandName($, 'theorem_definition'),
field('options', optional($.brack_group_key_value)),
field('name', $.curly_group_key),
optional(
choice(
seq(
field('heading', $.curly_group),
field('counter', optional($.brack_group_key)),
),
seq(
field('counter', $.brack_group_key),
field('heading', $.curly_group),
),
),
),
),
),
color_definition: $ =>
seq(
commandName($, 'color_definition'),
field('kind', optional($.brack_group_key)),
field('name', $.curly_group_key),
field('model', $.curly_group_key),
field('spec', $.curly_group_text),
),
color_set_definition: $ =>
seq(
commandName($, 'color_set_definition'),
field('kind', optional($.brack_group_key)),
field('model', $.curly_group_key_list),
field('head', $.curly_group),
field('tail', $.curly_group),
field('spec', $.curly_group),
),
color_reference: $ =>
seq(commandName($, 'color_reference'), field('name', $.curly_group_key)),
tikz_library_import: $ =>
seq(
commandName($, 'tikz_library_import'),
field('name', $.curly_group_key),
),
};

@ -0,0 +1,66 @@
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
const { commandName } = require('../util');
const verbatimEnvironment = (prefix, name, lexerRule) => ({
[`_${prefix}_environment`]: $ =>
seq(
field('begin', alias($[`_${prefix}_begin`], $.begin)),
alias($[lexerRule], $.comment),
field('end', alias($[`_${prefix}_end`], $.end)),
),
[`_${prefix}_begin`]: $ =>
seq(
commandName($, 'begin'),
field('name', alias($[`_${prefix}_curly_group_key`], $.curly_group_key)),
field('options', optional($.brack_group)),
),
[`_${prefix}_end`]: $ =>
seq(
commandName($, 'end'),
field('name', alias($[`_${prefix}_curly_group_key`], $.curly_group_key)),
),
[`_${prefix}_curly_group_key`]: $ =>
seq(
field('left', '{'),
field('key', alias($[`_${prefix}_key`], $.key)),
field('right', '}'),
),
[`_${prefix}_key`]: $ => seq(field('word', alias(name, $.word))),
});
module.exports = {
_environment: $ =>
choice(
$._comment_environment,
$._verbatim_environment,
$._listing_environment,
$._generic_environment,
),
// Regular environment
_generic_environment: $ =>
seq(field('begin', $.begin), repeat($._full_content), field('end', $.end)),
begin: $ =>
prec.right(
seq(
commandName($, 'begin'),
field('name', $.curly_group_key),
field('options', optional($.brack_group)),
),
),
end: $ =>
prec.right(seq(commandName($, 'end'), field('name', $.curly_group_key))),
// Verbatim environments
...verbatimEnvironment('comment', 'comment', '_verbatim_env_comment'),
...verbatimEnvironment('verbatim', 'verbatim', '_verbatim_env_verbatim'),
...verbatimEnvironment('listing', 'lstlisting', '_verbatim_env_listing'),
};

@ -0,0 +1,81 @@
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
const { sepBy } = require('../util');
const group = (left, content, right) =>
seq(field('left', left), content, field('right', right));
module.exports = {
curly_group: $ => group('{', repeat($._full_content), '}'),
curly_group_key: $ => group('{', field('key', $.key), '}'),
curly_group_key_list: $ => group('{', sepBy(field('key', $.key), ','), '}'),
curly_group_key_value: $ =>
group('{', sepBy(field('property', $.key_value_pair), ','), '}'),
curly_group_path: $ => group('{', field('path', $.path), '}'),
curly_group_path_list: $ =>
group('{', sepBy(field('path', $.path), ','), '}'),
curly_group_text: $ => group('{', field('text', $.text), '}'),
curly_group_command_name: $ =>
group(
'{',
field('command_name', alias($._generic_command_name, $.command_name)),
'}',
),
curly_group_impl: $ => group('{', repeat($._minimal_content), '}'),
curly_group_glob_pattern: $ =>
group('{', field('glob_pattern', $.glob_pattern), '}'),
brack_group: $ =>
prec.right(
group(
'[',
repeat(
choice($._basic_content, $.brack_group, $._contextual_delimiter),
),
']',
),
),
brack_group_key: $ => prec.right(1, group('[', field('key', $.key), ']')),
brack_group_key_value: $ =>
group('[', sepBy(field('property', $.key_value_pair), ','), ']'),
brack_group_argc: $ => group('[', field('argc', $.argc), ']'),
mixed_group: $ =>
group(
choice('[', '('),
repeat(choice($._basic_content, $.mixed_group, choice(',', '='))),
choice(']', ')'),
),
key_value_pair: $ =>
seq(field('key', $.key), optional(seq('=', field('value', $.value)))),
value: $ =>
prec.right(
repeat1(
choice(
$.curly_group,
$.displayed_equation,
$.inline_formula,
$.text,
$.macro_parameter,
$._command,
choice('(', ')'),
$.brack_group,
),
),
),
};

@ -0,0 +1,62 @@
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
const { commandName } = require('../util');
const basicInclude = ($, rule) =>
seq(
commandName($, rule),
field('options', optional($.brack_group_key_value)),
field('path', $.curly_group_path),
);
module.exports = {
_include: $ =>
choice(
$.package_include,
$.class_include,
$.latex_include,
$.biblatex_include,
$.bibtex_include,
$.graphics_include,
$.svg_include,
$.inkscape_include,
$.verbatim_include,
$.import,
),
package_include: $ =>
seq(
commandName($, 'package_include'),
field('options', optional($.brack_group_key_value)),
field('paths', $.curly_group_path_list),
),
class_include: $ => basicInclude($, 'class_include'),
latex_include: $ => basicInclude($, 'latex_include'),
biblatex_include: $ =>
seq(
commandName($, 'biblatex_include'),
field('options', optional($.brack_group_key_value)),
field('glob_pattern', $.curly_group_glob_pattern),
),
bibtex_include: $ => basicInclude($, 'bibtex_include'),
graphics_include: $ => basicInclude($, 'graphics_include'),
svg_include: $ => basicInclude($, 'svg_include'),
inkscape_include: $ => basicInclude($, 'inkscape_include'),
verbatim_include: $ => basicInclude($, 'verbatim_include'),
import: $ =>
seq(
commandName($, 'import'),
field('directory', $.curly_group_path),
field('file', $.curly_group_path),
),
};

@ -0,0 +1,22 @@
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
const commands = require('./commands');
const environments = require('./environments');
const groups = require('./groups');
const includes = require('./includes');
const math = require('./math');
const sections = require('./sections');
const text = require('./text');
const trivia = require('./trivia');
module.exports = {
...commands,
...environments,
...groups,
...includes,
...math,
...sections,
...text,
...trivia,
};

@ -0,0 +1,26 @@
module.exports = {
displayed_equation: $ =>
prec.left(
seq(
choice('$$', alias($._escaped_lbrack_command_name, $.command_name)),
repeat($._full_content),
choice('$$', alias($._escaped_rbrack_command_name, $.command_name)),
),
),
inline_formula: $ =>
prec.left(
seq(
choice('$', alias($._escaped_lparen_command_name, $.command_name)),
repeat($._full_content),
choice('$', alias($._escaped_rparen_command_name, $.command_name)),
),
),
math_set: $ =>
seq(
field('left', alias($._escaped_lcurly_command_name, $.command_name)),
repeat($._full_content),
field('right', alias($._escaped_rcurly_command_name, $.command_name)),
),
};

@ -0,0 +1,76 @@
/// <reference types="tree-sitter-cli/dsl" />
const { commandName } = require('../util');
// @ts-check
const SECTIONS = [
{
rule: 'part',
group: 'curly_group',
},
{
rule: 'chapter',
group: 'curly_group',
},
{
rule: 'section',
group: 'curly_group',
},
{
rule: 'subsection',
group: 'curly_group',
},
{
rule: 'subsubsection',
group: 'curly_group',
},
{
rule: 'paragraph',
group: 'curly_group',
},
{
rule: 'subparagraph',
group: 'curly_group',
},
{
rule: 'enum_item',
group: 'brack_group_key',
},
];
const rules = {};
(() => {
for (let i = 0; i < SECTIONS.length; i++) {
const { rule, group } = SECTIONS[i];
let start = i + 1;
const layer = `_${rule}_layer`;
rules[layer] = $ => {
return prec.right(
choice(
...SECTIONS.slice(start).map(section => repeat1($[section.rule])),
),
);
};
rules[rule] = $ => {
return prec.right(
1,
seq(
commandName($, rule),
field('heading', optional($[group])),
repeat(choice($._basic_content, $._contextual_delimiter, '[', ']')),
optional($[layer]),
),
);
};
}
rules._section = $ =>
prec.right(choice(...SECTIONS.map(({ rule }) => repeat1($[rule]))));
})();
module.exports = { ...rules };

@ -0,0 +1,26 @@
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
module.exports = {
key: $ => $._words,
text: $ => $._words,
_words: $ => prec.right(repeat1(field('word', $.word))),
word: $ => /[^\s\\%\{\},\$\[\]\(\)=\#]+/,
glob_pattern: $ => repeat1($._glob_pattern_fragment),
_glob_pattern_fragment: $ =>
choice(
seq('{', repeat($._glob_pattern_fragment), '}'),
/[^\"\[\]:;\|\{\}<>]+/,
),
path: $ => /[^\*\"\[\]:;,\|\{\}<>]+/,
argc: $ => /\d/,
macro_parameter: $ => /#\d/,
};

@ -0,0 +1,15 @@
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
module.exports = {
_whitespace: $ => /\s+/,
line_comment: $ => /%[^\r\n]*/,
block_comment: $ =>
seq(
field('left', alias($._iffalse_command_name, $.command_name)),
field('comment', optional(alias($._verbatim_cmd_fi, $.comment))),
field('right', alias($._fi_command_name, $.command_name)),
),
};

6102
src/grammar.json vendored

File diff suppressed because it is too large Load Diff

8495
src/node-types.json vendored

File diff suppressed because it is too large Load Diff

667829
src/parser.c vendored

File diff suppressed because it is too large Load Diff

456
src/scanner.c vendored

@ -0,0 +1,456 @@
#include <stdlib.h>
#include <string.h>
#include <tree_sitter/parser.h>
#define ALPHABET_SIZE 128
typedef struct trie {
struct trie *children[ALPHABET_SIZE];
int value;
} trie;
static trie *trie_new() {
trie *root = (trie *)malloc(sizeof(trie));
root->value = 0;
memset(root->children, 0, sizeof(trie *) * ALPHABET_SIZE);
return root;
}
static void trie_insert(trie *root, char *key, int value) {
trie *node = root;
for (int i = 0; key[i]; i++) {
int c = (int)key[i];
if (!node->children[c]) {
node->children[c] = trie_new();
}
node = node->children[c];
}
node->value = value;
}
static void trie_insert_group(trie *root, char **keys, int value) {
for (int i = 0; keys[i]; i++) {
trie_insert(root, keys[i], value);
}
}
static trie *trie_lookup(trie *root, int32_t c) {
return root && (c < ALPHABET_SIZE) ? root->children[c] : NULL;
}
static void trie_free(trie *root) {
for (int i = 0; i < ALPHABET_SIZE; i++) {
if (root->children[i]) {
trie_free(root->children[i]);
}
}
free(root);
}
static char *citation_commands[] = {
"cite", "cite*", "Cite", "nocite", "citet",
"citep", "citet*", "citep*", "citeauthor", "citeauthor*",
"Citeauthor", "Citeauthor*", "citetitle", "citetitle*", "citeyear",
"citeyear*", "citedate", "citedate*", "citeurl", "fullcite",
"citeyearpar", "citealt", "citealp", "citetext", "parencite",
"parencite*", "Parencite", "footcite", "footfullcite", "footcitetext",
"textcite", "Textcite", "smartcite", "Smartcite", "supercite",
"autocite", "Autocite", "autocite*", "Autocite*", "volcite",
"Volcite", "pvolcite", "Pvolcite", "fvolcite", "ftvolcite",
"svolcite", "Svolcite", "tvolcite", "Tvolcite", "avolcite",
"Avolcite", "notecite", "notecite", "pnotecite", "Pnotecite",
"fnotecite", NULL};
static char *label_reference_commands[] = {
"ref", "eqref", "vref", "Vref", "autoref",
"pageref", "cref", "Cref", "cref*", "Cref*",
"namecref", "nameCref", "lcnamecref", "namecrefs", "nameCrefs",
"lcnamecrefs", "labelcref", "labelcpageref", NULL};
static char *label_reference_range_commands[] = {
"crefrange", "crefrange", "Crefrange", "Crefrange", "crefrange*",
"crefrange*", "Crefrange*", "Crefrange*", NULL};
static char *new_command_definition_commands[] = {
"newcommand", "newcommand*", "renewcommand",
"renewcommand*", "DeclareRobustCommand", "DeclareRobustCommand*",
"DeclareMathOperator", "DeclareMathOperator*", NULL};
static char *glossary_entry_reference_commands[] = {"gls",
"Gls",
"GLS",
"glspl",
"Glspl",
"GLSpl",
"glsdisp",
"glslink",
"glstext",
"Glstext",
"GLStext",
"glsfirst",
"Glsfirst",
"GLSfirst",
"glsplural",
"Glsplural",
"GLSplural",
"glsfirstplural",
"Glsfirstplural",
"GLSfirstplural",
"glsname",
"Glsname",
"GLSname",
"glssymbol",
"Glssymbol",
"glsdesc",
"Glsdesc",
"GLSdesc",
"glsuseri",
"Glsuseri",
"GLSuseri",
"glsuserii",
"Glsuserii",
"GLSuserii",
"glsuseriii",
"Glsuseriii",
"GLSuseriii",
"glsuseriv",
"Glsuseriv",
"GLSuseriv",
"glsuserv",
"Glsuserv",
"GLSuserv",
"glsuservi",
"Glsuservi",
"GLSuservi",
NULL};
static char *acronym_reference_commands[] = {"acrshort",
"Acrshort",
"ACRshort",
"acrshortpl",
"Acrshortpl",
"ACRshortpl",
"acrlong",
"Acrlong",
"ACRlong",
"acrlongpl",
"Acrlongpl",
"ACRlongpl",
"acrfull",
"Acrfull",
"ACRfull",
"acrfullpl",
"Acrfullpl",
"ACRfullpl",
"acs",
"Acs",
"acsp",
"Acsp",
"acl",
"Acl",
"aclp",
"Aclp",
"acf",
"Acf",
"acfp",
"Acfp",
"ac",
"Ac",
"acp",
"glsentrylong",
"Glsentrylong",
"glsentrylongpl",
"Glsentrylongpl",
"glsentryshort",
"Glsentryshort",
"glsentryshortpl",
"Glsentryshortpl",
"glsentryfullpl",
"Glsentryfullpl",
NULL};
static char *theorem_definition_commands[] = {"newtheorem", "declaretheorem",
NULL};
static char *color_reference_commands[] = {"color", "colorbox", "textcolor",
"pagecolor", NULL};
static char *tikz_library_import_commands[] = {"usepgflibrary",
"usetikzlibrary", NULL};
static char *package_include_commands[] = {"usepackage", "RequirePackage",
NULL};
static char *latex_include_commands[] = {"include", "subfileinclude", "input",
"subfile", NULL};
static char *verbatim_include_commmands[] = {"verbatiminput", "VerbatimInput",
NULL};
static char *import_commands[] = {
"import", "subimport", "inputfrom", "subimportfrom",
"includefrom", "subincludefrom", NULL};
static char *part_commands[] = {"part", "part*", NULL};
static char *chapter_commands[] = {"chapter", "chapter*", NULL};
static char *section_commands[] = {"section", "section*", NULL};
static char *subsection_commands[] = {"subsection", "subsection*", NULL};
static char *subsubsection_commands[] = {"subsubsection", "subsubsection*",
NULL};
static char *paragraph_commands[] = {"paragraph", "paragraph*", NULL};
static char *subparagraph_commands[] = {"subparagraph", "subparagraph*", NULL};
static char *enum_item_commands[] = {"item", NULL};
enum TokenType {
CMD_GENERIC,
CMD_BEGIN,
CMD_END,
CMD_IFFALSE,
CMD_FI,
CMD_CAPTION,
CMD_CITATION,
CMD_LABEL_DEFINITION,
CMD_LABEL_REFERENCE,
CMD_LABEL_REFERENCE_RANGE,
CMD_LABEL_NUMBER,
CMD_NEW_CMD_DEFINITION,
CMD_OLD_CMD_DEFINITION,
CMD_LET_CMD_DEFINITION,
CMD_ENV_DEFINITION,
CMD_GLOSSARY_ENTRY_DEFINITION,
CMD_GLOSSARY_ENTRY_REFERENCE,
CMD_ACRONYM_DEFINITION,
CMD_ACRONYM_REFERENCE,
CMD_THEOREM_DEFINITION,
CMD_COLOR_DEFINITION,
CMD_COLOR_SET_DEFINITION,
CMD_COLOR_REFERENCE,
CMD_TIKZ_LIBRARY_IMPORT,
CMD_PACKAGE_INCLUDE,
CMD_CLASS_INCLUDE,
CMD_LATEX_INCLUDE,
CMD_BIBLATEX_INCLUDE,
CMD_BIBTEX_INCLUDE,
CMD_GRAPHICS_INCLUDE,
CMD_SVG_INCLUDE,
CMD_INKSCAPE_INCLUDE,
CMD_VERBATIM_INCLUDE,
CMD_IMPORT,
CMD_ESCAPED_LPAREN,
CMD_ESCAPED_RPAREN,
CMD_ESCAPED_LBRACK,
CMD_ESCAPED_RBRACK,
CMD_ESCAPED_LCURLY,
CMD_ESCAPED_RCURLY,
CMD_PART,
CMD_CHAPTER,
CMD_SECTION,
CMD_SUBSECTION,
CMD_SUBSUBSECTION,
CMD_PARAGRAPH,
CMD_SUBPARAGRAPH,
CMD_ENUM_ITEM,
VERBATIM_CMD_FI,
VERBATIM_ENV_COMMENT,
VERBATIM_ENV_VERBATIM,
VERBATIM_ENV_LISTING,
VERBATIM_ENV_MINTED,
};
static bool is_part_of_command_name(int32_t c) {
switch (c) {
case '_':
case ':':
case '*':
return true;
default:
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
}
static int scan_command_name(TSLexer *lexer, trie *commands) {
trie *node = commands;
bool escape = true;
lexer->advance(lexer, false);
while (!lexer->eof(lexer)) {
int c = lexer->lookahead;
if (!is_part_of_command_name(c)) {
break;
}
node = trie_lookup(node, c);
lexer->advance(lexer, false);
escape = false;
}
if (!lexer->eof(lexer)) {
int c = lexer->lookahead;
if (c != '\r' && c != '\n' && (escape || c == '*')) {
node = trie_lookup(node, c);
lexer->advance(lexer, false);
}
}
return node ? node->value : CMD_GENERIC;
}
static bool scan_keyword(TSLexer *lexer, const char *keyword) {
if (keyword) {
for (int i = 0; keyword[i]; i++) {
if (lexer->eof(lexer) || lexer->lookahead != keyword[i]) {
return false;
}
lexer->advance(lexer, false);
}
}
return true;
}
static void scan_verbatim(TSLexer *lexer, trie *commands, int end_command_type,
const char *end_keyword) {
while (!lexer->eof(lexer)) {
while (!lexer->eof(lexer) && lexer->lookahead != '\\') {
lexer->advance(lexer, false);
}
lexer->mark_end(lexer);
if (lexer->eof(lexer)) {
break;
}
int command_type = scan_command_name(lexer, commands);
if (command_type == end_command_type && scan_keyword(lexer, end_keyword)) {
break;
}
}
}
void *tree_sitter_latex_external_scanner_create() {
trie *commands = trie_new();
trie_insert(commands, "begin", CMD_BEGIN);
trie_insert(commands, "end", CMD_END);
trie_insert(commands, "iffalse", CMD_IFFALSE);
trie_insert(commands, "fi", CMD_FI);
trie_insert(commands, "caption", CMD_CAPTION);
trie_insert_group(commands, citation_commands, CMD_CITATION);
trie_insert(commands, "label", CMD_LABEL_DEFINITION);
trie_insert_group(commands, label_reference_commands, CMD_LABEL_REFERENCE);
trie_insert_group(commands, label_reference_range_commands,
CMD_LABEL_REFERENCE_RANGE);
trie_insert(commands, "newlabel", CMD_LABEL_NUMBER);
trie_insert_group(commands, new_command_definition_commands,
CMD_NEW_CMD_DEFINITION);
trie_insert(commands, "def", CMD_OLD_CMD_DEFINITION);
trie_insert(commands, "let", CMD_LET_CMD_DEFINITION);
trie_insert(commands, "newenvironment", CMD_ENV_DEFINITION);
trie_insert(commands, "newglossaryentry", CMD_GLOSSARY_ENTRY_DEFINITION);
trie_insert_group(commands, glossary_entry_reference_commands,
CMD_GLOSSARY_ENTRY_REFERENCE);
trie_insert(commands, "newacronym", CMD_ACRONYM_DEFINITION);
trie_insert_group(commands, acronym_reference_commands,
CMD_ACRONYM_REFERENCE);
trie_insert_group(commands, theorem_definition_commands,
CMD_THEOREM_DEFINITION);
trie_insert(commands, "definecolor", CMD_COLOR_DEFINITION);
trie_insert(commands, "definecolorset", CMD_COLOR_SET_DEFINITION);
trie_insert_group(commands, color_reference_commands, CMD_COLOR_REFERENCE);
trie_insert_group(commands, tikz_library_import_commands,
CMD_TIKZ_LIBRARY_IMPORT);
trie_insert_group(commands, package_include_commands, CMD_PACKAGE_INCLUDE);
trie_insert(commands, "documentclass", CMD_CLASS_INCLUDE);
trie_insert_group(commands, latex_include_commands, CMD_LATEX_INCLUDE);
trie_insert(commands, "addbibresource", CMD_BIBLATEX_INCLUDE);
trie_insert(commands, "bibliography", CMD_BIBTEX_INCLUDE);
trie_insert(commands, "includegraphics", CMD_GRAPHICS_INCLUDE);
trie_insert(commands, "includesvg", CMD_SVG_INCLUDE);
trie_insert(commands, "includeinkscape", CMD_INKSCAPE_INCLUDE);
trie_insert_group(commands, verbatim_include_commmands, CMD_VERBATIM_INCLUDE);
trie_insert_group(commands, import_commands, CMD_IMPORT);
trie_insert(commands, "(", CMD_ESCAPED_LPAREN);
trie_insert(commands, ")", CMD_ESCAPED_RPAREN);
trie_insert(commands, "[", CMD_ESCAPED_LBRACK);
trie_insert(commands, "]", CMD_ESCAPED_RBRACK);
trie_insert(commands, "{", CMD_ESCAPED_LCURLY);
trie_insert(commands, "}", CMD_ESCAPED_RCURLY);
trie_insert_group(commands, part_commands, CMD_PART);
trie_insert_group(commands, chapter_commands, CMD_CHAPTER);
trie_insert_group(commands, section_commands, CMD_SECTION);
trie_insert_group(commands, subsection_commands, CMD_SUBSECTION);
trie_insert_group(commands, subsubsection_commands, CMD_SUBSUBSECTION);
trie_insert_group(commands, paragraph_commands, CMD_PARAGRAPH);
trie_insert_group(commands, subparagraph_commands, CMD_SUBPARAGRAPH);
trie_insert_group(commands, enum_item_commands, CMD_ENUM_ITEM);
return commands;
}
void tree_sitter_latex_external_scanner_destroy(void *payload) {
trie_free((trie *)payload);
}
unsigned tree_sitter_latex_external_scanner_serialize(void *payload,
char *buffer) {
return 0;
}
void tree_sitter_latex_external_scanner_deserialize(void *payload,
const char *buffer,
unsigned length) {}
bool tree_sitter_latex_external_scanner_scan(void *payload, TSLexer *lexer,
const bool *valid_symbols) {
trie *commands = (trie *)payload;
for (int i = VERBATIM_CMD_FI; i <= VERBATIM_ENV_MINTED; i++) {
if (valid_symbols[i]) {
for (int j = i + 1; j <= VERBATIM_ENV_MINTED; j++) {
if (valid_symbols[j]) {
return false;
}
}
lexer->result_symbol = i;
switch (i) {
case VERBATIM_CMD_FI:
scan_verbatim(lexer, commands, CMD_FI, NULL);
break;
case VERBATIM_ENV_COMMENT:
scan_verbatim(lexer, commands, CMD_END, "{comment}");
break;
case VERBATIM_ENV_VERBATIM:
scan_verbatim(lexer, commands, CMD_END, "{verbatim}");
break;
case VERBATIM_ENV_LISTING:
scan_verbatim(lexer, commands, CMD_END, "{lstlisting}");
break;
case VERBATIM_ENV_MINTED:
scan_verbatim(lexer, commands, CMD_END, "{comment}");
break;
}
return true;
}
}
if (lexer->lookahead != '\\') {
return false;
}
int command_type = scan_command_name(lexer, commands);
lexer->mark_end(lexer);
lexer->result_symbol =
valid_symbols[command_type] ? command_type : CMD_GENERIC;
return true;
}

@ -0,0 +1,117 @@
================================================================================
Command without arguments
================================================================================
\foo bar
--------------------------------------------------------------------------------
(source_file
(command
(command_name))
(text
(word)))
================================================================================
Command with one curly argument
================================================================================
\foo{bar}
--------------------------------------------------------------------------------
(source_file
(command
(command_name)
(curly_group
(text
(word)))))
================================================================================
Command with one curly argument and one mixed argument
================================================================================
\foo{bar}[baz]
--------------------------------------------------------------------------------
(source_file
(command
(command_name)
(curly_group
(text
(word)))
(mixed_group
(text
(word)))))
================================================================================
Command with nested paren argument
================================================================================
\foo([bar])
--------------------------------------------------------------------------------
(source_file
(command
(command_name)
(mixed_group
(mixed_group
(text
(word))))))
================================================================================
Command with incomplete argument
================================================================================
\foo{ \begin{document} \end{document}
--------------------------------------------------------------------------------
(source_file
(command
(command_name))
(ERROR
(environment
(begin
(command_name)
(curly_group_key
(key
(word))))
(end
(command_name)
(curly_group_key
(key
(word)))))))
================================================================================
Acroynm definition
================================================================================
\newacronym[foo={bar}]{gcd}{GCD}{Greatest Common Divisor}
--------------------------------------------------------------------------------
(source_file
(acronym_definition
(command_name)
(brack_group_key_value
(key_value_pair
(key
(word))
(value
(curly_group
(text
(word))))))
(curly_group_key
(key
(word)))
(curly_group
(text
(word)))
(curly_group
(text
(word)
(word)
(word)))))

@ -1,11 +0,0 @@
================================================================================
Issue #7 (env vars in paths)
================================================================================
\addbibresource{$HOME/ref.bib}
--------------------------------------------------------------------------------
(document
(biblatex_include
(path)))

@ -0,0 +1,202 @@
================================================================================
Simple environment
================================================================================
\begin{document}
Hello World!
\end{document}
--------------------------------------------------------------------------------
(source_file
(environment
(begin
(command_name)
(curly_group_key
(key
(word))))
(text
(word)
(word))
(end
(command_name)
(curly_group_key
(key
(word))))))
================================================================================
Environment with options
================================================================================
\begin{document}[foo bar]
Hello World!
\end{document}
--------------------------------------------------------------------------------
(source_file
(environment
(begin
(command_name)
(curly_group_key
(key
(word)))
(brack_group
(text
(word)
(word))))
(text
(word)
(word))
(end
(command_name)
(curly_group_key
(key
(word))))))
================================================================================
Environment with nested options
================================================================================
\begin{document}[foo [bar] (baz)]
Hello World!
\end{document}
--------------------------------------------------------------------------------
(source_file
(environment
(begin
(command_name)
(curly_group_key
(key
(word)))
(brack_group
(text
(word))
(brack_group
(text
(word)))
(text
(word))))
(text
(word)
(word))
(end
(command_name)
(curly_group_key
(key
(word))))))
================================================================================
Environment with escaped options
================================================================================
\begin{document}[{[}{]}]
Hello World!
\end{document}
--------------------------------------------------------------------------------
(source_file
(environment
(begin
(command_name)
(curly_group_key
(key
(word)))
(brack_group
(curly_group)
(curly_group)))
(text
(word)
(word))
(end
(command_name)
(curly_group_key
(key
(word))))))
================================================================================
Comment environment
================================================================================
Foo
\begin{comment}
\begin{document}
Hello World
\end{document}
\end{comment}
Bar
--------------------------------------------------------------------------------
(source_file
(text
(word))
(environment
(begin
(command_name)
(curly_group_key
(key
(word))))
(comment)
(end
(command_name)
(curly_group_key
(key
(word)))))
(text
(word)))
================================================================================
Verbatim environment
================================================================================
Foo
\begin{verbatim}
\begin{document}
Hello World
\end{document}
\end{verbatim}
Bar
--------------------------------------------------------------------------------
(source_file
(text
(word))
(environment
(begin
(command_name)
(curly_group_key
(key
(word))))
(comment)
(end
(command_name)
(curly_group_key
(key
(word)))))
(text
(word)))

@ -0,0 +1,34 @@
================================================================================
Nested curly groups
================================================================================
Foo {Bar Baz {Qux}}
--------------------------------------------------------------------------------
(source_file
(text
(word))
(curly_group
(text
(word)
(word))
(curly_group
(text
(word)))))
================================================================================
Unbalanced curly braces
================================================================================
} Foo { Bar
--------------------------------------------------------------------------------
(source_file
(ERROR)
(text
(word))
(ERROR
(text
(word))))

@ -0,0 +1,199 @@
================================================================================
Simple package include
================================================================================
\usepackage{amsmath}
--------------------------------------------------------------------------------
(source_file
(package_include
(command_name)
(curly_group_path_list
(path))))
================================================================================
Package include with two paths
================================================================================
\usepackage{amsmath,lipsum}
--------------------------------------------------------------------------------
(source_file
(package_include
(command_name)
(curly_group_path_list
(path)
(path))))
================================================================================
Package include with options
================================================================================
\usepackage[foo=.5\textwidth, bar baz]{qux}
--------------------------------------------------------------------------------
(source_file
(package_include
(command_name)
(brack_group_key_value
(key_value_pair
(key
(word))
(value
(text
(word))
(command
(command_name))))
(key_value_pair
(key
(word)
(word))))
(curly_group_path_list
(path))))
================================================================================
Simple class include
================================================================================
\documentclass{amsmath}
--------------------------------------------------------------------------------
(source_file
(class_include
(command_name)
(curly_group_path
(path))))
================================================================================
Class include with two paths
================================================================================
\documentclass{amsmath,lipsum}
--------------------------------------------------------------------------------
(source_file
(class_include
(command_name)
(curly_group_path
(path)
(MISSING "}")))
(text
(word))
(ERROR))
================================================================================
Class include with options
================================================================================
\documentclass[foo=.5\textwidth, bar baz]{qux}
--------------------------------------------------------------------------------
(source_file
(class_include
(command_name)
(brack_group_key_value
(key_value_pair
(key
(word))
(value
(text
(word))
(command
(command_name))))
(key_value_pair
(key
(word)
(word))))
(curly_group_path
(path))))
================================================================================
Simple LaTeX include
================================================================================
\input{foo.tex}
--------------------------------------------------------------------------------
(source_file
(latex_include
(command_name)
(curly_group_path
(path))))
================================================================================
Empty LaTeX include
================================================================================
\input{}
--------------------------------------------------------------------------------
(source_file
(ERROR
(command_name)))
================================================================================
BibLaTeX glob include
================================================================================
\addbibresource[glob]{foo.{1,2,3}.bib}
--------------------------------------------------------------------------------
(source_file
(biblatex_include
(command_name)
(brack_group_key_value
(key_value_pair
(key
(word))))
(curly_group_glob_pattern
(glob_pattern))))
================================================================================
BibTeX include with absolute path
================================================================================
\bibliography{/home/user/foo.bib}
--------------------------------------------------------------------------------
(source_file
(bibtex_include
(command_name)
(curly_group_path
(path))))
================================================================================
Graphics include with options
================================================================================
\includegraphics[scale=0.5,angle=45]{test}
--------------------------------------------------------------------------------
(source_file
(graphics_include
(command_name)
(brack_group_key_value
(key_value_pair
(key
(word))
(value
(text
(word))))
(key_value_pair
(key
(word))
(value
(text
(word)))))
(curly_group_path
(path))))

@ -0,0 +1,260 @@
================================================================================
tree-sitter-latex (Issue #11)
================================================================================
\documentclass{article}
\begin{document}
\texttt{foo :("@@@} foo foo
foo foo \texttt{0xffe00fff} foo foo foo foo foo foo foo foo foo foo foo
foo \texttt{0xfff00000}.
foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo
foo. foo foo foo foo foo foo foo foo foo foo
foo foo foo foo foo foo
\end{document}
--------------------------------------------------------------------------------
(source_file
(class_include
(command_name)
(curly_group_path
(path)))
(environment
(begin
(command_name)
(curly_group_key
(key
(word))))
(command
(command_name)
(curly_group
(text
(word)
(word))
(text
(word))))
(text
(word)
(word)
(word)
(word))
(command
(command_name)
(curly_group
(text
(word))))
(text
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word))
(command
(command_name)
(curly_group
(text
(word))))
(text
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word)
(word))
(end
(command_name)
(curly_group_key
(key
(word))))))
================================================================================
tree-sitter-latex (Issue #15)
================================================================================
\iffalse
something here
\fi
\usepackage{comment} % in the preamble
\begin{comment}
something
\end{comment}
--------------------------------------------------------------------------------
(source_file
(block_comment
(command_name)
(comment)
(command_name))
(package_include
(command_name)
(curly_group_path_list
(path)))
(line_comment)
(environment
(begin
(command_name)
(curly_group_key
(key
(word))))
(comment)
(end
(command_name)
(curly_group_key
(key
(word))))))
================================================================================
tree-sitter-latex (Issue #10)
================================================================================
\titleformat{\section}{\relax}{\thesection}{1em}{\spacedsmallcaps}
\titlespacing*{\paragraph}{0pt}{1\baselineskip}{0.5\baselineskip}
--------------------------------------------------------------------------------
(source_file
(command
(command_name)
(curly_group
(section
(command_name)))
(curly_group
(command
(command_name)))
(curly_group
(command
(command_name)))
(curly_group
(text
(word)))
(curly_group
(command
(command_name))))
(command
(command_name)
(curly_group
(paragraph
(command_name)))
(curly_group
(text
(word)))
(curly_group
(text
(word))
(command
(command_name)))
(curly_group
(text
(word))
(command
(command_name)))))
================================================================================
tree-sitter-latex (Issue #7)
================================================================================
\addbibresource{$HOME/ref.bib}
To clear \lstinline{biber} cache, run
\begin{lstlisting}[language=bash]
rm -rf "$(biber --cache)"
\end{lstlisting}
--------------------------------------------------------------------------------
(source_file
(biblatex_include
(command_name)
(curly_group_glob_pattern
(glob_pattern)))
(text
(word)
(word))
(command
(command_name)
(curly_group
(text
(word))))
(text
(word))
(text
(word))
(environment
(begin
(command_name)
(curly_group_key
(key
(word))))
(comment)
(end
(command_name)
(curly_group_key
(key
(word))))))
================================================================================
tree-sitter-latex (Issue #11)
================================================================================
\newenvironment{mycenter}
{\begin{center}}
{\end{center}}
--------------------------------------------------------------------------------
(source_file
(environment_definition
(command_name)
(curly_group_key
(key
(word)))
(curly_group_impl
(command
(command_name)
(curly_group
(text
(word)))))
(curly_group_impl
(command
(command_name)
(curly_group
(text
(word)))))))

@ -0,0 +1,216 @@
================================================================================
Simple displayed equation (dollar)
================================================================================
Foo $$ Bar Baz $$
--------------------------------------------------------------------------------
(source_file
(text
(word))
(displayed_equation
(text
(word)
(word))))
================================================================================
Simple displayed equation (command)
================================================================================
Foo \[ Bar Baz \]
--------------------------------------------------------------------------------
(source_file
(text
(word))
(displayed_equation
(command_name)
(text
(word)
(word))
(command_name)))
================================================================================
Displayed equation (mixed)
================================================================================
Foo \[ Bar Baz $$
--------------------------------------------------------------------------------
(source_file
(text
(word))
(displayed_equation
(command_name)
(text
(word)
(word))))
================================================================================
Displayed equation (nested)
================================================================================
Foo $$ Bar Baz \[ Qux \] $$
--------------------------------------------------------------------------------
(source_file
(text
(word))
(displayed_equation
(text
(word)
(word))
(displayed_equation
(command_name)
(text
(word))
(command_name))))
================================================================================
Displayed equation (flat)
================================================================================
Foo $$ Bar $$ Baz $$ Qux $$
--------------------------------------------------------------------------------
(source_file
(text
(word))
(displayed_equation
(text
(word)))
(text
(word))
(displayed_equation
(text
(word))))
================================================================================
Simple inline formula (dollar)
================================================================================
Foo $ Bar Baz $
--------------------------------------------------------------------------------
(source_file
(text
(word))
(inline_formula
(text
(word)
(word))))
================================================================================
Simple inline formula (command)
================================================================================
Foo \( Bar Baz \)
--------------------------------------------------------------------------------
(source_file
(text
(word))
(inline_formula
(command_name)
(text
(word)
(word))
(command_name)))
================================================================================
Inline formula (mixed)
================================================================================
Foo \( Bar Baz $
--------------------------------------------------------------------------------
(source_file
(text
(word))
(inline_formula
(command_name)
(text
(word)
(word))))
================================================================================
Inline formula (nested)
================================================================================
Foo $ Bar Baz \( Qux \) $
--------------------------------------------------------------------------------
(source_file
(text
(word))
(inline_formula
(text
(word)
(word))
(inline_formula
(command_name)
(text
(word))
(command_name))))
================================================================================
Inline formula (flat)
================================================================================
Foo $ Bar $ Baz $ Qux $
--------------------------------------------------------------------------------
(source_file
(text
(word))
(inline_formula
(text
(word)))
(text
(word))
(inline_formula
(text
(word))))
================================================================================
Inline formula closed with double dollar
================================================================================
$ foo $$ bar
--------------------------------------------------------------------------------
(source_file
(ERROR
(text
(word))
(word)))
================================================================================
Simple math set
================================================================================
\{ 1, 2, 3 \}
--------------------------------------------------------------------------------
(source_file
(math_set
(command_name)
(text
(word))
(text
(word))
(text
(word))
(command_name)))

@ -0,0 +1,94 @@
================================================================================
Top level sections
================================================================================
\begin{document}
1
\section{Foo}
2
\section{Bar}
3
\end{document}
--------------------------------------------------------------------------------
(source_file
(environment
(begin
(command_name)
(curly_group_key
(key
(word))))
(text
(word))
(section
(command_name)
(curly_group
(text
(word)))
(text
(word)))
(section
(command_name)
(curly_group
(text
(word)))
(text
(word)))
(end
(command_name)
(curly_group_key
(key
(word))))))
================================================================================
Nested sections
================================================================================
1
\part{\Foo}
2
\chapter{Bar}
3
\chapter{{Baz}}
4
--------------------------------------------------------------------------------
(source_file
(text
(word))
(part
(command_name)
(curly_group
(command
(command_name)))
(text
(word))
(chapter
(command_name)
(curly_group
(text
(word)))
(text
(word)))
(chapter
(command_name)
(curly_group
(curly_group
(text
(word))))
(text
(word)))))

@ -0,0 +1,89 @@
================================================================================
Hello world
================================================================================
Hello World!
--------------------------------------------------------------------------------
(source_file
(text
(word)
(word)))
================================================================================
Matched parens and brackets
================================================================================
Foo Bar (Baz [(Qux)])
--------------------------------------------------------------------------------
(source_file
(text
(word)
(word))
(text
(word))
(text
(word)))
================================================================================
Unmatched parens
================================================================================
Foo ) Bar ( Baz
--------------------------------------------------------------------------------
(source_file
(text
(word))
(text
(word))
(text
(word)))
================================================================================
Comma separated words
================================================================================
Foo, Bar
--------------------------------------------------------------------------------
(source_file
(text
(word))
(text
(word)))
================================================================================
Valid macro parameter
================================================================================
Macro #1 parameter
--------------------------------------------------------------------------------
(source_file
(text
(word))
(macro_parameter)
(text
(word)))
================================================================================
Invalid macro parameter
================================================================================
Foo # Bar
--------------------------------------------------------------------------------
(source_file
(text
(word)
(ERROR
(UNEXPECTED ' '))
(word)))

@ -0,0 +1,54 @@
================================================================================
Line comment
================================================================================
% Foo
Bar
--------------------------------------------------------------------------------
(source_file
(line_comment)
(text
(word)))
================================================================================
Escaped percent
================================================================================
\% Foo
--------------------------------------------------------------------------------
(source_file
(command
(command_name))
(text
(word)))
================================================================================
Block comment
================================================================================
Hello World
\iffalse
\foo Bar \begin{Baz
\fi
Test
--------------------------------------------------------------------------------
(source_file
(text
(word)
(word))
(block_comment
(command_name)
(comment)
(command_name))
(text
(word)))

@ -0,0 +1,15 @@
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check
const sepBy1 = (rule, sep) => seq(rule, repeat(seq(sep, rule)));
const sepBy = (rule, sep) => optional(sepBy1(rule, sep));
const commandName = ($, rule) =>
field('command_name', alias($[`_${rule}_command_name`], $.command_name));
module.exports = {
sepBy1,
sepBy,
commandName,
};