syntax_lang/lib.rs
1//! # syntax_lang
2//!
3//! A lossless concrete syntax tree (CST) with trivia — the substrate a formatter,
4//! a language server, or a tree-sitter-style generator builds on. It owns no
5//! grammar: a language brings its own kind type, and syntax-lang supplies the tree
6//! shape, the builder that assembles it, and the traversals over it.
7//!
8//! ## Model
9//!
10//! A tree is [`Node`]s all the way down to [`Token`] leaves. Each [`Node`] carries
11//! a kind, the [`Span`] of source it covers, and an ordered list of [`Element`]
12//! children — each child being a nested node or a leaf token. Node kinds and token
13//! kinds share one type `K` (the rowan model): a language defines a single `enum`
14//! with both composite variants (`Expr`, `Root`) and lexical ones (`Ident`,
15//! `Plus`, `Whitespace`), and implements [`TokenKind`] on it to mark trivia and the
16//! end marker.
17//!
18//! *Lossless* means nothing is discarded. Trivia — whitespace and comments — is not
19//! filtered out; it rides as ordinary leaf tokens in source order. So the tree is a
20//! faithful record of the source: slicing the original text by a node's span, or
21//! concatenating the node's [`tokens`](Node::tokens), reproduces exactly the source
22//! that node came from. Tokens store spans rather than copies of the text, so the
23//! tree stays compact and the reconstruction is a zero-copy borrow.
24//!
25//! ## Building a tree
26//!
27//! A parser drives a [`Builder`] with three moves — open a node, push a token,
28//! close a node:
29//!
30//! ```
31//! use syntax_lang::{Builder, Span, Token, TokenKind};
32//!
33//! // One kind type for both nodes and tokens.
34//! #[derive(Clone, Copy, Debug, PartialEq, Eq)]
35//! enum Kind {
36//! // nodes
37//! Root,
38//! Sum,
39//! // tokens
40//! Num,
41//! Plus,
42//! Space,
43//! }
44//!
45//! impl TokenKind for Kind {
46//! fn is_trivia(&self) -> bool {
47//! matches!(self, Kind::Space)
48//! }
49//! }
50//!
51//! // Build the CST for `1 + 2`, keeping the spaces as trivia tokens.
52//! let mut b = Builder::new();
53//! b.start_node(Kind::Root);
54//! b.start_node(Kind::Sum);
55//! b.token(Token::new(Kind::Num, Span::new(0, 1)));
56//! b.token(Token::new(Kind::Space, Span::new(1, 2)));
57//! b.token(Token::new(Kind::Plus, Span::new(2, 3)));
58//! b.token(Token::new(Kind::Space, Span::new(3, 4)));
59//! b.token(Token::new(Kind::Num, Span::new(4, 5)));
60//! b.finish_node(); // close Sum
61//! b.finish_node(); // close Root
62//!
63//! let root = b.finish().expect("balanced");
64//!
65//! // Lossless: the tree reproduces the source, trivia and all.
66//! assert_eq!(root.text("1 + 2"), Some("1 + 2"));
67//! assert_eq!(root.tokens().count(), 5);
68//!
69//! // A formatter can now walk the significant tokens and skip the trivia.
70//! let significant = root.tokens().filter(|t| !t.is_trivia()).count();
71//! assert_eq!(significant, 3);
72//! ```
73//!
74//! ## Walking a tree
75//!
76//! [`Node::tokens`] yields every leaf in source order (the lossless stream);
77//! [`Node::descendants`] yields every node in pre-order; [`Node::children`],
78//! [`Node::child_nodes`], and [`Node::child_tokens`] step over the direct level.
79//! All of these are iterative — a tree tens of thousands of levels deep is walked,
80//! and freed, without overflowing the call stack.
81//!
82//! ## Features
83//!
84//! - `std` (default) — the standard library; without it the crate is `no_std` (it
85//! always needs `alloc` for the child vectors). Forwards to `token-lang/std` and
86//! `span-lang/std`.
87//!
88//! ## Stability
89//!
90//! The public surface is frozen as of `1.0.0` and follows Semantic Versioning: no
91//! breaking change before `2.0`, additions arrive in minor releases, and the MSRV
92//! (Rust 1.85) only rises in a minor. The frozen surface is catalogued in
93//! [`docs/API.md`](https://github.com/jamesgober/syntax-lang/blob/main/docs/API.md).
94
95#![cfg_attr(not(feature = "std"), no_std)]
96#![cfg_attr(docsrs, feature(doc_cfg))]
97#![deny(missing_docs)]
98#![forbid(unsafe_code)]
99
100extern crate alloc;
101
102mod build;
103mod tree;
104
105pub use build::{BuildError, Builder};
106pub use tree::{Element, Node};
107
108// Re-exported so a downstream defining and walking a CST can name the token and
109// position types this crate's API is built on without also depending on
110// `token-lang` and `span-lang` directly.
111pub use span_lang::{Span, Spanned};
112pub use token_lang::{Symbol, Token, TokenKind};