Skip to main content

tree_sitter_mosaic/
lib.rs

1//! This crate provides Mosaic language support for the [tree-sitter] parsing library.
2//!
3//! Typically, you will use the [`LANGUAGE`] constant to add this language to a
4//! tree-sitter [`Parser`], and then use the parser to parse some code:
5//!
6//! ```
7//! let code = "= Hello\n\nA short paragraph.\n";
8//! let mut parser = tree_sitter::Parser::new();
9//! let language = tree_sitter_mosaic::LANGUAGE;
10//! parser
11//!     .set_language(&language.into())
12//!     .expect("Error loading Mosaic parser");
13//! let tree = parser.parse(code, None).unwrap();
14//! assert!(!tree.root_node().has_error());
15//! ```
16//!
17//! [`Parser`]: https://docs.rs/tree-sitter/0.26.8/tree_sitter/struct.Parser.html
18//! [tree-sitter]: https://tree-sitter.github.io/
19
20#![doc(
21    html_logo_url = "https://mosaic.kjanat.dev/assets/A4.svg",
22    html_favicon_url = "https://mosaic.kjanat.dev/assets/A4.svg"
23)]
24
25use tree_sitter_language::LanguageFn;
26
27// SAFETY: `tree_sitter_mosaic` is the C entry point emitted by
28// `tree-sitter generate` into `src/parser.c` and linked by `build.rs`.
29// Its signature matches what `tree-sitter` expects from a grammar.
30#[allow(
31    unsafe_code,
32    reason = "FFI declaration of the generated C parser entry point"
33)]
34unsafe extern "C" {
35    fn tree_sitter_mosaic() -> *const ();
36}
37
38/// The tree-sitter [`LanguageFn`] for this grammar.
39#[allow(
40    unsafe_code,
41    reason = "Wrap C parser entry point in tree_sitter_language::LanguageFn"
42)]
43// SAFETY: `tree_sitter_mosaic` returns a pointer to a `TSLanguage` whose
44// shape matches the ABI declared by the linked `tree_sitter_language`
45// crate; wrapping it in `LanguageFn::from_raw` is the documented way to
46// expose a generated grammar to consumers.
47pub const LANGUAGE: LanguageFn = unsafe { LanguageFn::from_raw(tree_sitter_mosaic) };
48
49/// The content of the [`node-types.json`] file for this grammar.
50///
51/// [`node-types.json`]: https://tree-sitter.github.io/tree-sitter/using-parsers/6-static-node-types
52pub const NODE_TYPES: &str = include_str!("../../src/node-types.json");
53
54#[cfg(with_highlights_query)]
55/// The syntax highlighting query for this grammar.
56pub const HIGHLIGHTS_QUERY: &str = include_str!("../../queries/highlights.scm");
57
58#[cfg(with_injections_query)]
59/// The language injection query for this grammar.
60pub const INJECTIONS_QUERY: &str = include_str!("../../queries/injections.scm");
61
62#[cfg(with_locals_query)]
63/// The local variable query for this grammar.
64pub const LOCALS_QUERY: &str = include_str!("../../queries/locals.scm");
65
66#[cfg(with_tags_query)]
67/// The symbol tagging query for this grammar.
68pub const TAGS_QUERY: &str = include_str!("../../queries/tags.scm");
69
70#[cfg(test)]
71mod tests {
72    fn parse_sexp(source: &str) -> Option<String> {
73        let mut parser = tree_sitter::Parser::new();
74        let language = super::LANGUAGE.into();
75        let language_result = parser.set_language(&language);
76        assert!(
77            language_result.is_ok(),
78            "Error loading Mosaic parser: {:?}",
79            language_result.err()
80        );
81
82        let tree = parser.parse(source, None);
83        assert!(tree.is_some(), "parser returned no tree");
84        let tree = tree?;
85        let root = tree.root_node();
86        assert!(
87            !root.has_error(),
88            "unexpected parse error: {}",
89            root.to_sexp()
90        );
91        Some(root.to_sexp())
92    }
93
94    #[test]
95    fn test_can_load_grammar() {
96        let mut parser = tree_sitter::Parser::new();
97        let language = super::LANGUAGE.into();
98        let language_result = parser.set_language(&language);
99
100        assert!(
101            language_result.is_ok(),
102            "Error loading Mosaic parser: {:?}",
103            language_result.err()
104        );
105    }
106
107    #[test]
108    fn parses_byte_zero_shebang_with_crlf() {
109        let sexp = parse_sexp("#!/usr/bin/env -S mos build --open\r\n= Hello\n");
110
111        assert_eq!(
112            sexp.as_deref(),
113            Some(
114                "(source_file (shebang) (section (section1 (heading marker: (heading_marker) content: (inline_sequence (text))))))"
115            )
116        );
117    }
118}