From 3309dd889c5cd7beb4000d55a3940dcd056095bd Mon Sep 17 00:00:00 2001 From: Sergei Zharinov Date: Fri, 9 Jan 2026 10:43:26 -0300 Subject: [PATCH] refactor: Replace proc-macro with build.rs-based grammar/node-types embedding --- Cargo.lock | 218 ++++----- Cargo.toml | 2 +- crates/plotnik-core/Cargo.toml | 2 + crates/plotnik-core/src/grammar/binary.rs | 16 + .../plotnik-core/src/grammar/binary_tests.rs | 39 ++ crates/plotnik-core/src/grammar/json.rs | 255 ++++++++++ crates/plotnik-core/src/grammar/json_tests.rs | 74 +++ crates/plotnik-core/src/grammar/mod.rs | 16 + crates/plotnik-core/src/grammar/types.rs | 114 +++++ crates/plotnik-core/src/lib.rs | 7 +- crates/plotnik-langs/Cargo.toml | 202 ++++---- crates/plotnik-langs/build.rs | 85 +++- crates/plotnik-langs/src/builtin.rs | 223 ++++----- crates/plotnik-langs/src/lib.rs | 41 +- crates/plotnik-macros/Cargo.toml | 217 --------- crates/plotnik-macros/src/lib.rs | 449 ------------------ 16 files changed, 954 insertions(+), 1006 deletions(-) create mode 100644 crates/plotnik-core/src/grammar/binary.rs create mode 100644 crates/plotnik-core/src/grammar/binary_tests.rs create mode 100644 crates/plotnik-core/src/grammar/json.rs create mode 100644 crates/plotnik-core/src/grammar/json_tests.rs create mode 100644 crates/plotnik-core/src/grammar/mod.rs create mode 100644 crates/plotnik-core/src/grammar/types.rs delete mode 100644 crates/plotnik-macros/Cargo.toml delete mode 100644 crates/plotnik-macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2fe81ea6..2adcf35e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1195,6 +1195,15 @@ dependencies = [ "tree-sitter-language", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "beef" version = "0.5.2" @@ -1207,6 +1216,12 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "camino" version = "1.2.1" @@ -1295,6 +1310,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -1328,6 +1352,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "dlmalloc" version = "0.2.12" @@ -1339,6 +1369,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "encode_unicode" version = "1.0.0" @@ -1391,6 +1433,15 @@ dependencies = [ "wasip2", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1403,6 +1454,20 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version", + "serde", + "spin", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -1417,6 +1482,8 @@ checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -1465,6 +1532,15 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "logos" version = "0.16.0" @@ -1550,6 +1626,8 @@ dependencies = [ name = "plotnik-core" version = "0.2.0" dependencies = [ + "indexmap", + "postcard", "serde", "serde_json", ] @@ -1660,7 +1738,9 @@ dependencies = [ "cargo_metadata", "paste", "plotnik-core", - "plotnik-macros", + "postcard", + "serde", + "serde_json", ] [[package]] @@ -1685,113 +1765,16 @@ dependencies = [ ] [[package]] -name = "plotnik-macros" -version = "0.2.0" +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" dependencies = [ - "arborium-ada", - "arborium-agda", - "arborium-asciidoc", - "arborium-asm", - "arborium-awk", - "arborium-bash", - "arborium-batch", - "arborium-c", - "arborium-c-sharp", - "arborium-caddy", - "arborium-capnp", - "arborium-clojure", - "arborium-cmake", - "arborium-commonlisp", - "arborium-cpp", - "arborium-css", - "arborium-d", - "arborium-dart", - "arborium-devicetree", - "arborium-diff", - "arborium-dockerfile", - "arborium-dot", - "arborium-elisp", - "arborium-elixir", - "arborium-elm", - "arborium-erlang", - "arborium-fish", - "arborium-fsharp", - "arborium-gleam", - "arborium-glsl", - "arborium-go", - "arborium-graphql", - "arborium-groovy", - "arborium-haskell", - "arborium-hcl", - "arborium-hlsl", - "arborium-html", - "arborium-idris", - "arborium-ini", - "arborium-java", - "arborium-javascript", - "arborium-jinja2", - "arborium-jq", - "arborium-json", - "arborium-julia", - "arborium-kdl", - "arborium-kotlin", - "arborium-lean", - "arborium-lua", - "arborium-markdown", - "arborium-matlab", - "arborium-meson", - "arborium-nginx", - "arborium-ninja", - "arborium-nix", - "arborium-objc", - "arborium-ocaml", - "arborium-perl", - "arborium-php", - "arborium-postscript", - "arborium-powershell", - "arborium-prolog", - "arborium-python", - "arborium-query", - "arborium-r", - "arborium-rescript", - "arborium-ron", - "arborium-ruby", - "arborium-rust", - "arborium-scala", - "arborium-scheme", - "arborium-scss", - "arborium-sparql", - "arborium-sql", - "arborium-ssh-config", - "arborium-starlark", - "arborium-svelte", - "arborium-swift", - "arborium-textproto", - "arborium-thrift", - "arborium-tlaplus", - "arborium-toml", - "arborium-tree-sitter", - "arborium-tsx", - "arborium-typescript", - "arborium-typst", - "arborium-uiua", - "arborium-vb", - "arborium-verilog", - "arborium-vhdl", - "arborium-vim", - "arborium-vue", - "arborium-wit", - "arborium-x86asm", - "arborium-xml", - "arborium-yaml", - "arborium-yuri", - "arborium-zig", - "arborium-zsh", - "plotnik-core", - "proc-macro2", - "quote", - "serde_json", - "syn", + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", ] [[package]] @@ -1893,6 +1876,12 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "semver" version = "1.0.27" @@ -1958,6 +1947,21 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "streaming-iterator" version = "0.1.9" diff --git a/Cargo.toml b/Cargo.toml index fe776c4f..ed38b535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,4 +2,4 @@ resolver = "2" -members = ["crates/plotnik-cli", "crates/plotnik-lib", "crates/plotnik-langs", "crates/plotnik-macros", "crates/plotnik-core"] +members = ["crates/plotnik-cli", "crates/plotnik-lib", "crates/plotnik-langs", "crates/plotnik-core"] diff --git a/crates/plotnik-core/Cargo.toml b/crates/plotnik-core/Cargo.toml index 5355dbeb..3a88bfdb 100644 --- a/crates/plotnik-core/Cargo.toml +++ b/crates/plotnik-core/Cargo.toml @@ -10,5 +10,7 @@ repository = "https://github.com/plotnik-lang/plotnik" unexpected_cfgs = { level = "warn", check-cfg = ['cfg(coverage_nightly)'] } [dependencies] +indexmap = { version = "2", features = ["serde"] } +postcard = { version = "1", features = ["alloc"] } serde = { version = "1", features = ["derive"] } serde_json = "1" \ No newline at end of file diff --git a/crates/plotnik-core/src/grammar/binary.rs b/crates/plotnik-core/src/grammar/binary.rs new file mode 100644 index 00000000..9394fd55 --- /dev/null +++ b/crates/plotnik-core/src/grammar/binary.rs @@ -0,0 +1,16 @@ +//! Binary serialization for grammars using postcard. + +use super::json::GrammarError; +use super::types::Grammar; + +impl Grammar { + /// Deserialize grammar from binary format. + pub fn from_binary(bytes: &[u8]) -> Result { + postcard::from_bytes(bytes).map_err(GrammarError::Binary) + } + + /// Serialize grammar to binary format. + pub fn to_binary(&self) -> Vec { + postcard::to_allocvec(self).expect("serialization should not fail") + } +} diff --git a/crates/plotnik-core/src/grammar/binary_tests.rs b/crates/plotnik-core/src/grammar/binary_tests.rs new file mode 100644 index 00000000..809ce2ff --- /dev/null +++ b/crates/plotnik-core/src/grammar/binary_tests.rs @@ -0,0 +1,39 @@ +use super::*; + +#[test] +fn roundtrip() { + let json = r#"{ + "name": "test", + "rules": { + "source_file": { "type": "SYMBOL", "name": "expression" }, + "expression": { "type": "STRING", "value": "x" } + } + }"#; + + let grammar = Grammar::from_json(json).unwrap(); + let binary = grammar.to_binary(); + let decoded = Grammar::from_binary(&binary).unwrap(); + + assert_eq!(grammar.name, decoded.name); + assert_eq!(grammar.rules.len(), decoded.rules.len()); +} + +#[test] +fn roundtrip_preserves_order() { + let json = r#"{ + "name": "test", + "rules": { + "program": { "type": "SYMBOL", "name": "statement" }, + "statement": { "type": "SYMBOL", "name": "expression" }, + "expression": { "type": "STRING", "value": "x" } + } + }"#; + + let grammar = Grammar::from_json(json).unwrap(); + let binary = grammar.to_binary(); + let decoded = Grammar::from_binary(&binary).unwrap(); + + assert_eq!(decoded.rules[0].0, "program"); + assert_eq!(decoded.rules[1].0, "statement"); + assert_eq!(decoded.rules[2].0, "expression"); +} diff --git a/crates/plotnik-core/src/grammar/json.rs b/crates/plotnik-core/src/grammar/json.rs new file mode 100644 index 00000000..bf817e05 --- /dev/null +++ b/crates/plotnik-core/src/grammar/json.rs @@ -0,0 +1,255 @@ +//! JSON deserialization for grammar.json files. +//! +//! Tree-sitter's grammar.json uses externally-tagged enums with `type` field. + +use indexmap::IndexMap; +use serde::Deserialize; + +use super::types::{Grammar, Precedence, PrecedenceEntry, Rule}; + +/// Error during grammar parsing. +#[derive(Debug)] +pub enum GrammarError { + Json(serde_json::Error), + Binary(postcard::Error), +} + +impl std::fmt::Display for GrammarError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Json(e) => write!(f, "JSON parse error: {e}"), + Self::Binary(e) => write!(f, "binary decode error: {e}"), + } + } +} + +impl std::error::Error for GrammarError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Json(e) => Some(e), + Self::Binary(e) => Some(e), + } + } +} + +impl Grammar { + /// Parse grammar from JSON string. + pub fn from_json(json: &str) -> Result { + let raw: RawGrammar = serde_json::from_str(json).map_err(GrammarError::Json)?; + Ok(raw.into()) + } +} + +/// Raw grammar structure matching tree-sitter's JSON format. +#[derive(Debug, Deserialize)] +struct RawGrammar { + name: String, + rules: IndexMap, + #[serde(default)] + extras: Vec, + #[serde(default)] + precedences: Vec>, + #[serde(default)] + conflicts: Vec>, + #[serde(default)] + externals: Vec, + #[serde(default, rename = "inline")] + inline_rules: Vec, + #[serde(default)] + supertypes: Vec, + #[serde(default)] + word: Option, + #[serde(default)] + reserved: IndexMap>, + #[serde(default)] + inherits: Option, +} + +impl From for Grammar { + fn from(raw: RawGrammar) -> Self { + // IndexMap preserves insertion order, which matches tree-sitter's definition order. + // The entry rule is always first. + Self { + name: raw.name, + rules: raw.rules.into_iter().map(|(k, v)| (k, v.into())).collect(), + extras: raw.extras.into_iter().map(Into::into).collect(), + precedences: raw + .precedences + .into_iter() + .map(|v| v.into_iter().map(Into::into).collect()) + .collect(), + conflicts: raw.conflicts, + externals: raw.externals.into_iter().map(Into::into).collect(), + inline: raw.inline_rules, + supertypes: raw.supertypes, + word: raw.word, + reserved: raw + .reserved + .into_iter() + .map(|(k, v)| (k, v.into_iter().map(Into::into).collect())) + .collect(), + inherits: raw.inherits, + } + } +} + +/// Raw rule matching tree-sitter's JSON format. +#[derive(Debug, Deserialize)] +#[serde(tag = "type")] +#[allow(clippy::upper_case_acronyms, non_camel_case_types)] +enum RawRule { + BLANK, + STRING { + value: String, + }, + PATTERN { + value: String, + #[serde(default)] + flags: Option, + }, + SYMBOL { + name: String, + }, + SEQ { + members: Vec, + }, + CHOICE { + members: Vec, + }, + REPEAT { + content: Box, + }, + REPEAT1 { + content: Box, + }, + FIELD { + name: String, + content: Box, + }, + ALIAS { + content: Box, + value: String, + named: bool, + }, + TOKEN { + content: Box, + }, + IMMEDIATE_TOKEN { + content: Box, + }, + PREC { + value: RawPrecedence, + content: Box, + }, + PREC_LEFT { + value: RawPrecedence, + content: Box, + }, + PREC_RIGHT { + value: RawPrecedence, + content: Box, + }, + PREC_DYNAMIC { + value: i32, + content: Box, + }, + RESERVED { + context_name: String, + content: Box, + }, +} + +impl From for Rule { + fn from(raw: RawRule) -> Self { + #[allow(clippy::boxed_local)] // Fields are Box, output needs Box + fn conv(content: Box) -> Box { + Box::new(Rule::from(*content)) + } + + match raw { + RawRule::BLANK => Rule::Blank, + RawRule::STRING { value } => Rule::String(value), + RawRule::PATTERN { value, flags } => Rule::Pattern { value, flags }, + RawRule::SYMBOL { name } => Rule::Symbol(name), + RawRule::SEQ { members } => Rule::Seq(members.into_iter().map(Into::into).collect()), + RawRule::CHOICE { members } => { + Rule::Choice(members.into_iter().map(Into::into).collect()) + } + RawRule::REPEAT { content } => Rule::Repeat(conv(content)), + RawRule::REPEAT1 { content } => Rule::Repeat1(conv(content)), + RawRule::FIELD { name, content } => Rule::Field { + name, + content: conv(content), + }, + RawRule::ALIAS { + content, + value, + named, + } => Rule::Alias { + content: conv(content), + value, + named, + }, + RawRule::TOKEN { content } => Rule::Token(conv(content)), + RawRule::IMMEDIATE_TOKEN { content } => Rule::ImmediateToken(conv(content)), + RawRule::PREC { value, content } => Rule::Prec { + value: value.into(), + content: conv(content), + }, + RawRule::PREC_LEFT { value, content } => Rule::PrecLeft { + value: value.into(), + content: conv(content), + }, + RawRule::PREC_RIGHT { value, content } => Rule::PrecRight { + value: value.into(), + content: conv(content), + }, + RawRule::PREC_DYNAMIC { value, content } => Rule::PrecDynamic { + value, + content: conv(content), + }, + RawRule::RESERVED { + context_name, + content, + } => Rule::Reserved { + context_name, + content: conv(content), + }, + } + } +} + +/// Raw precedence value (can be integer or string). +#[derive(Debug, Deserialize)] +#[serde(untagged)] +enum RawPrecedence { + Integer(i32), + Name(String), +} + +impl From for Precedence { + fn from(raw: RawPrecedence) -> Self { + match raw { + RawPrecedence::Integer(n) => Precedence::Integer(n), + RawPrecedence::Name(s) => Precedence::Name(s), + } + } +} + +/// Raw precedence entry (STRING or SYMBOL). +#[derive(Debug, Deserialize)] +#[serde(tag = "type")] +#[allow(clippy::upper_case_acronyms)] +enum RawPrecedenceEntry { + STRING { value: String }, + SYMBOL { name: String }, +} + +impl From for PrecedenceEntry { + fn from(raw: RawPrecedenceEntry) -> Self { + match raw { + RawPrecedenceEntry::STRING { value } => PrecedenceEntry::Name(value), + RawPrecedenceEntry::SYMBOL { name } => PrecedenceEntry::Symbol(name), + } + } +} diff --git a/crates/plotnik-core/src/grammar/json_tests.rs b/crates/plotnik-core/src/grammar/json_tests.rs new file mode 100644 index 00000000..d1052157 --- /dev/null +++ b/crates/plotnik-core/src/grammar/json_tests.rs @@ -0,0 +1,74 @@ +use super::*; + +#[test] +fn parse_minimal_grammar() { + let json = r#"{ + "name": "test", + "rules": { + "source_file": { "type": "SYMBOL", "name": "expression" }, + "expression": { "type": "STRING", "value": "x" } + } + }"#; + + let grammar = Grammar::from_json(json).unwrap(); + assert_eq!(grammar.name, "test"); + assert_eq!(grammar.rules.len(), 2); +} + +#[test] +fn parse_seq_and_choice() { + let json = r#"{ + "name": "test", + "rules": { + "root": { + "type": "SEQ", + "members": [ + { "type": "STRING", "value": "a" }, + { "type": "CHOICE", "members": [ + { "type": "STRING", "value": "b" }, + { "type": "BLANK" } + ]} + ] + } + } + }"#; + + let grammar = Grammar::from_json(json).unwrap(); + assert!(matches!(grammar.rules[0].1, Rule::Seq(_))); +} + +#[test] +fn parse_field() { + let json = r#"{ + "name": "test", + "rules": { + "func": { + "type": "FIELD", + "name": "name", + "content": { "type": "SYMBOL", "name": "identifier" } + } + } + }"#; + + let grammar = Grammar::from_json(json).unwrap(); + assert!(matches!(grammar.rules[0].1, Rule::Field { .. })); +} + +#[test] +fn preserves_rule_order() { + let json = r#"{ + "name": "test", + "rules": { + "program": { "type": "SYMBOL", "name": "statement" }, + "statement": { "type": "SYMBOL", "name": "expression" }, + "expression": { "type": "STRING", "value": "x" } + } + }"#; + + let grammar = Grammar::from_json(json).unwrap(); + + // Entry rule should be first (program), not alphabetically sorted + assert_eq!(grammar.rules[0].0, "program"); + assert_eq!(grammar.rules[1].0, "statement"); + assert_eq!(grammar.rules[2].0, "expression"); +} diff --git a/crates/plotnik-core/src/grammar/mod.rs b/crates/plotnik-core/src/grammar/mod.rs new file mode 100644 index 00000000..a7eeee19 --- /dev/null +++ b/crates/plotnik-core/src/grammar/mod.rs @@ -0,0 +1,16 @@ +//! Grammar types for tree-sitter grammars. +//! +//! This module provides types for representing tree-sitter `grammar.json` files, +//! with support for JSON deserialization and compact binary serialization. + +mod binary; +mod json; +mod types; + +#[cfg(test)] +mod binary_tests; +#[cfg(test)] +mod json_tests; + +pub use json::GrammarError; +pub use types::{Grammar, Precedence, PrecedenceEntry, Rule}; diff --git a/crates/plotnik-core/src/grammar/types.rs b/crates/plotnik-core/src/grammar/types.rs new file mode 100644 index 00000000..e74ab318 --- /dev/null +++ b/crates/plotnik-core/src/grammar/types.rs @@ -0,0 +1,114 @@ +//! Grammar type definitions. + +use serde::{Deserialize, Serialize}; + +/// Complete tree-sitter grammar. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Grammar { + /// Grammar name (e.g., "javascript", "rust"). + pub name: String, + /// Production rules, preserving definition order. + pub rules: Vec<(String, Rule)>, + /// Extra/trivia nodes (comments, whitespace). + #[serde(default)] + pub extras: Vec, + /// Precedence orderings. + #[serde(default)] + pub precedences: Vec>, + /// Expected conflicts. + #[serde(default)] + pub conflicts: Vec>, + /// External scanner tokens. + #[serde(default)] + pub externals: Vec, + /// Rules to inline (hidden). + #[serde(default)] + pub inline: Vec, + /// Supertype rules. + #[serde(default)] + pub supertypes: Vec, + /// Keyword identifier rule. + #[serde(default)] + pub word: Option, + /// Reserved word contexts. + #[serde(default)] + pub reserved: Vec<(String, Vec)>, + /// Parent grammar name (for inheritance). + #[serde(default)] + pub inherits: Option, +} + +/// Grammar rule variants. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Rule { + /// Epsilon (empty match). + Blank, + /// Literal token. + String(String), + /// Regex token. + Pattern { + value: String, + #[serde(default)] + flags: Option, + }, + /// Reference to another rule. + Symbol(String), + /// Sequence of rules (must match in order). + Seq(Vec), + /// Alternation (first matching wins). + Choice(Vec), + /// Zero or more repetitions. + Repeat(Box), + /// One or more repetitions. + Repeat1(Box), + /// Named field. + Field { name: String, content: Box }, + /// Rename node. + Alias { + content: Box, + value: String, + named: bool, + }, + /// Force tokenization. + Token(Box), + /// Immediate tokenization. + ImmediateToken(Box), + /// Precedence. + Prec { + value: Precedence, + content: Box, + }, + /// Left-associative precedence. + PrecLeft { + value: Precedence, + content: Box, + }, + /// Right-associative precedence. + PrecRight { + value: Precedence, + content: Box, + }, + /// Dynamic precedence. + PrecDynamic { value: i32, content: Box }, + /// Reserved word context. + Reserved { + context_name: String, + content: Box, + }, +} + +/// Precedence value (numeric or named). +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum Precedence { + Integer(i32), + Name(String), +} + +/// Entry in precedence ordering. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum PrecedenceEntry { + /// Named precedence level. + Name(String), + /// Symbol reference. + Symbol(String), +} diff --git a/crates/plotnik-core/src/lib.rs b/crates/plotnik-core/src/lib.rs index c3eb9db9..f1ac8433 100644 --- a/crates/plotnik-core/src/lib.rs +++ b/crates/plotnik-core/src/lib.rs @@ -13,6 +13,7 @@ use std::collections::HashMap; use std::num::NonZeroU16; +pub mod grammar; mod interner; mod invariants; pub mod utils; @@ -27,7 +28,7 @@ mod utils_tests; pub use interner::{Interner, Symbol}; /// Raw node definition from `node-types.json`. -#[derive(Debug, Clone, serde::Deserialize)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct RawNode { #[serde(rename = "type")] pub type_name: String, @@ -43,7 +44,7 @@ pub struct RawNode { } /// Cardinality constraints for a field or children slot. -#[derive(Debug, Clone, serde::Deserialize)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct RawCardinality { pub multiple: bool, pub required: bool, @@ -51,7 +52,7 @@ pub struct RawCardinality { } /// Reference to a node type. -#[derive(Debug, Clone, serde::Deserialize)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct RawTypeRef { #[serde(rename = "type")] pub type_name: String, diff --git a/crates/plotnik-langs/Cargo.toml b/crates/plotnik-langs/Cargo.toml index 1b58e958..d770d140 100644 --- a/crates/plotnik-langs/Cargo.toml +++ b/crates/plotnik-langs/Cargo.toml @@ -132,109 +132,109 @@ all-languages = [ ] # Individual language features -lang-ada = ["dep:arborium-ada", "plotnik-macros/lang-ada"] -lang-agda = ["dep:arborium-agda", "plotnik-macros/lang-agda"] -lang-asciidoc = ["dep:arborium-asciidoc", "plotnik-macros/lang-asciidoc"] -lang-asm = ["dep:arborium-asm", "plotnik-macros/lang-asm"] -lang-awk = ["dep:arborium-awk", "plotnik-macros/lang-awk"] -lang-bash = ["dep:arborium-bash", "plotnik-macros/lang-bash"] -lang-batch = ["dep:arborium-batch", "plotnik-macros/lang-batch"] -lang-c = ["dep:arborium-c", "plotnik-macros/lang-c"] -lang-c-sharp = ["dep:arborium-c-sharp", "plotnik-macros/lang-c-sharp"] -lang-caddy = ["dep:arborium-caddy", "plotnik-macros/lang-caddy"] -lang-capnp = ["dep:arborium-capnp", "plotnik-macros/lang-capnp"] -lang-clojure = ["dep:arborium-clojure", "plotnik-macros/lang-clojure"] -lang-cmake = ["dep:arborium-cmake", "plotnik-macros/lang-cmake"] -lang-commonlisp = ["dep:arborium-commonlisp", "plotnik-macros/lang-commonlisp"] -lang-cpp = ["dep:arborium-cpp", "plotnik-macros/lang-cpp"] -lang-css = ["dep:arborium-css", "plotnik-macros/lang-css"] -lang-d = ["dep:arborium-d", "plotnik-macros/lang-d"] -lang-dart = ["dep:arborium-dart", "plotnik-macros/lang-dart"] -lang-devicetree = ["dep:arborium-devicetree", "plotnik-macros/lang-devicetree"] -lang-diff = ["dep:arborium-diff", "plotnik-macros/lang-diff"] -lang-dockerfile = ["dep:arborium-dockerfile", "plotnik-macros/lang-dockerfile"] -lang-dot = ["dep:arborium-dot", "plotnik-macros/lang-dot"] -lang-elisp = ["dep:arborium-elisp", "plotnik-macros/lang-elisp"] -lang-elixir = ["dep:arborium-elixir", "plotnik-macros/lang-elixir"] -lang-elm = ["dep:arborium-elm", "plotnik-macros/lang-elm"] -lang-erlang = ["dep:arborium-erlang", "plotnik-macros/lang-erlang"] -lang-fish = ["dep:arborium-fish", "plotnik-macros/lang-fish"] -lang-fsharp = ["dep:arborium-fsharp", "plotnik-macros/lang-fsharp"] -lang-gleam = ["dep:arborium-gleam", "plotnik-macros/lang-gleam"] -lang-glsl = ["dep:arborium-glsl", "plotnik-macros/lang-glsl"] -lang-go = ["dep:arborium-go", "plotnik-macros/lang-go"] -lang-graphql = ["dep:arborium-graphql", "plotnik-macros/lang-graphql"] -lang-groovy = ["dep:arborium-groovy", "plotnik-macros/lang-groovy"] -lang-haskell = ["dep:arborium-haskell", "plotnik-macros/lang-haskell"] -lang-hcl = ["dep:arborium-hcl", "plotnik-macros/lang-hcl"] -lang-hlsl = ["dep:arborium-hlsl", "plotnik-macros/lang-hlsl"] -lang-html = ["dep:arborium-html", "plotnik-macros/lang-html"] -lang-idris = ["dep:arborium-idris", "plotnik-macros/lang-idris"] -lang-ini = ["dep:arborium-ini", "plotnik-macros/lang-ini"] -lang-java = ["dep:arborium-java", "plotnik-macros/lang-java"] -lang-javascript = ["dep:arborium-javascript", "plotnik-macros/lang-javascript"] -lang-jinja2 = ["dep:arborium-jinja2", "plotnik-macros/lang-jinja2"] -lang-jq = ["dep:arborium-jq", "plotnik-macros/lang-jq"] -lang-json = ["dep:arborium-json", "plotnik-macros/lang-json"] -lang-julia = ["dep:arborium-julia", "plotnik-macros/lang-julia"] -lang-kdl = ["dep:arborium-kdl", "plotnik-macros/lang-kdl"] -lang-kotlin = ["dep:arborium-kotlin", "plotnik-macros/lang-kotlin"] -lang-lean = ["dep:arborium-lean", "plotnik-macros/lang-lean"] -lang-lua = ["dep:arborium-lua", "plotnik-macros/lang-lua"] -lang-markdown = ["dep:arborium-markdown", "plotnik-macros/lang-markdown"] -lang-matlab = ["dep:arborium-matlab", "plotnik-macros/lang-matlab"] -lang-meson = ["dep:arborium-meson", "plotnik-macros/lang-meson"] -lang-nginx = ["dep:arborium-nginx", "plotnik-macros/lang-nginx"] -lang-ninja = ["dep:arborium-ninja", "plotnik-macros/lang-ninja"] -lang-nix = ["dep:arborium-nix", "plotnik-macros/lang-nix"] -lang-objc = ["dep:arborium-objc", "plotnik-macros/lang-objc"] -lang-ocaml = ["dep:arborium-ocaml", "plotnik-macros/lang-ocaml"] -lang-perl = ["dep:arborium-perl", "plotnik-macros/lang-perl"] -lang-php = ["dep:arborium-php", "plotnik-macros/lang-php"] -lang-postscript = ["dep:arborium-postscript", "plotnik-macros/lang-postscript"] -lang-powershell = ["dep:arborium-powershell", "plotnik-macros/lang-powershell"] -lang-prolog = ["dep:arborium-prolog", "plotnik-macros/lang-prolog"] -lang-python = ["dep:arborium-python", "plotnik-macros/lang-python"] -lang-query = ["dep:arborium-query", "plotnik-macros/lang-query"] -lang-r = ["dep:arborium-r", "plotnik-macros/lang-r"] -lang-rescript = ["dep:arborium-rescript", "plotnik-macros/lang-rescript"] -lang-ron = ["dep:arborium-ron", "plotnik-macros/lang-ron"] -lang-ruby = ["dep:arborium-ruby", "plotnik-macros/lang-ruby"] -lang-rust = ["dep:arborium-rust", "plotnik-macros/lang-rust"] -lang-scala = ["dep:arborium-scala", "plotnik-macros/lang-scala"] -lang-scheme = ["dep:arborium-scheme", "plotnik-macros/lang-scheme"] -lang-scss = ["dep:arborium-scss", "plotnik-macros/lang-scss"] -lang-sparql = ["dep:arborium-sparql", "plotnik-macros/lang-sparql"] -lang-sql = ["dep:arborium-sql", "plotnik-macros/lang-sql"] -lang-ssh-config = ["dep:arborium-ssh-config", "plotnik-macros/lang-ssh-config"] -lang-starlark = ["dep:arborium-starlark", "plotnik-macros/lang-starlark"] -lang-svelte = ["dep:arborium-svelte", "plotnik-macros/lang-svelte"] -lang-swift = ["dep:arborium-swift", "plotnik-macros/lang-swift"] -lang-textproto = ["dep:arborium-textproto", "plotnik-macros/lang-textproto"] -lang-thrift = ["dep:arborium-thrift", "plotnik-macros/lang-thrift"] -lang-tlaplus = ["dep:arborium-tlaplus", "plotnik-macros/lang-tlaplus"] -lang-toml = ["dep:arborium-toml", "plotnik-macros/lang-toml"] -lang-tsx = ["dep:arborium-tsx", "plotnik-macros/lang-tsx"] -lang-typescript = ["dep:arborium-typescript", "plotnik-macros/lang-typescript"] -lang-typst = ["dep:arborium-typst", "plotnik-macros/lang-typst"] -lang-uiua = ["dep:arborium-uiua", "plotnik-macros/lang-uiua"] -lang-vb = ["dep:arborium-vb", "plotnik-macros/lang-vb"] -lang-verilog = ["dep:arborium-verilog", "plotnik-macros/lang-verilog"] -lang-vhdl = ["dep:arborium-vhdl", "plotnik-macros/lang-vhdl"] -lang-vim = ["dep:arborium-vim", "plotnik-macros/lang-vim"] -lang-vue = ["dep:arborium-vue", "plotnik-macros/lang-vue"] -lang-wit = ["dep:arborium-wit", "plotnik-macros/lang-wit"] -lang-x86asm = ["dep:arborium-x86asm", "plotnik-macros/lang-x86asm"] -lang-xml = ["dep:arborium-xml", "plotnik-macros/lang-xml"] -lang-yaml = ["dep:arborium-yaml", "plotnik-macros/lang-yaml"] -lang-yuri = ["dep:arborium-yuri", "plotnik-macros/lang-yuri"] -lang-zig = ["dep:arborium-zig", "plotnik-macros/lang-zig"] -lang-zsh = ["dep:arborium-zsh", "plotnik-macros/lang-zsh"] +lang-ada = ["dep:arborium-ada"] +lang-agda = ["dep:arborium-agda"] +lang-asciidoc = ["dep:arborium-asciidoc"] +lang-asm = ["dep:arborium-asm"] +lang-awk = ["dep:arborium-awk"] +lang-bash = ["dep:arborium-bash"] +lang-batch = ["dep:arborium-batch"] +lang-c = ["dep:arborium-c"] +lang-c-sharp = ["dep:arborium-c-sharp"] +lang-caddy = ["dep:arborium-caddy"] +lang-capnp = ["dep:arborium-capnp"] +lang-clojure = ["dep:arborium-clojure"] +lang-cmake = ["dep:arborium-cmake"] +lang-commonlisp = ["dep:arborium-commonlisp"] +lang-cpp = ["dep:arborium-cpp"] +lang-css = ["dep:arborium-css"] +lang-d = ["dep:arborium-d"] +lang-dart = ["dep:arborium-dart"] +lang-devicetree = ["dep:arborium-devicetree"] +lang-diff = ["dep:arborium-diff"] +lang-dockerfile = ["dep:arborium-dockerfile"] +lang-dot = ["dep:arborium-dot"] +lang-elisp = ["dep:arborium-elisp"] +lang-elixir = ["dep:arborium-elixir"] +lang-elm = ["dep:arborium-elm"] +lang-erlang = ["dep:arborium-erlang"] +lang-fish = ["dep:arborium-fish"] +lang-fsharp = ["dep:arborium-fsharp"] +lang-gleam = ["dep:arborium-gleam"] +lang-glsl = ["dep:arborium-glsl"] +lang-go = ["dep:arborium-go"] +lang-graphql = ["dep:arborium-graphql"] +lang-groovy = ["dep:arborium-groovy"] +lang-haskell = ["dep:arborium-haskell"] +lang-hcl = ["dep:arborium-hcl"] +lang-hlsl = ["dep:arborium-hlsl"] +lang-html = ["dep:arborium-html"] +lang-idris = ["dep:arborium-idris"] +lang-ini = ["dep:arborium-ini"] +lang-java = ["dep:arborium-java"] +lang-javascript = ["dep:arborium-javascript"] +lang-jinja2 = ["dep:arborium-jinja2"] +lang-jq = ["dep:arborium-jq"] +lang-json = ["dep:arborium-json"] +lang-julia = ["dep:arborium-julia"] +lang-kdl = ["dep:arborium-kdl"] +lang-kotlin = ["dep:arborium-kotlin"] +lang-lean = ["dep:arborium-lean"] +lang-lua = ["dep:arborium-lua"] +lang-markdown = ["dep:arborium-markdown"] +lang-matlab = ["dep:arborium-matlab"] +lang-meson = ["dep:arborium-meson"] +lang-nginx = ["dep:arborium-nginx"] +lang-ninja = ["dep:arborium-ninja"] +lang-nix = ["dep:arborium-nix"] +lang-objc = ["dep:arborium-objc"] +lang-ocaml = ["dep:arborium-ocaml"] +lang-perl = ["dep:arborium-perl"] +lang-php = ["dep:arborium-php"] +lang-postscript = ["dep:arborium-postscript"] +lang-powershell = ["dep:arborium-powershell"] +lang-prolog = ["dep:arborium-prolog"] +lang-python = ["dep:arborium-python"] +lang-query = ["dep:arborium-query"] +lang-r = ["dep:arborium-r"] +lang-rescript = ["dep:arborium-rescript"] +lang-ron = ["dep:arborium-ron"] +lang-ruby = ["dep:arborium-ruby"] +lang-rust = ["dep:arborium-rust"] +lang-scala = ["dep:arborium-scala"] +lang-scheme = ["dep:arborium-scheme"] +lang-scss = ["dep:arborium-scss"] +lang-sparql = ["dep:arborium-sparql"] +lang-sql = ["dep:arborium-sql"] +lang-ssh-config = ["dep:arborium-ssh-config"] +lang-starlark = ["dep:arborium-starlark"] +lang-svelte = ["dep:arborium-svelte"] +lang-swift = ["dep:arborium-swift"] +lang-textproto = ["dep:arborium-textproto"] +lang-thrift = ["dep:arborium-thrift"] +lang-tlaplus = ["dep:arborium-tlaplus"] +lang-toml = ["dep:arborium-toml"] +lang-tsx = ["dep:arborium-tsx"] +lang-typescript = ["dep:arborium-typescript"] +lang-typst = ["dep:arborium-typst"] +lang-uiua = ["dep:arborium-uiua"] +lang-vb = ["dep:arborium-vb"] +lang-verilog = ["dep:arborium-verilog"] +lang-vhdl = ["dep:arborium-vhdl"] +lang-vim = ["dep:arborium-vim"] +lang-vue = ["dep:arborium-vue"] +lang-wit = ["dep:arborium-wit"] +lang-x86asm = ["dep:arborium-x86asm"] +lang-xml = ["dep:arborium-xml"] +lang-yaml = ["dep:arborium-yaml"] +lang-yuri = ["dep:arborium-yuri"] +lang-zig = ["dep:arborium-zig"] +lang-zsh = ["dep:arborium-zsh"] [dependencies] paste = "1.0" plotnik-core = { version = "0.2", path = "../plotnik-core" } -plotnik-macros = { version = "0.2", path = "../plotnik-macros" } +postcard = { version = "1", features = ["alloc"] } arborium-tree-sitter = "2.5.0" arborium-ada = { version = "2.5.0", optional = true } arborium-agda = { version = "2.5.0", optional = true } @@ -337,5 +337,9 @@ arborium-zsh = { version = "2.5.0", optional = true } [build-dependencies] cargo_metadata = "0.23" +plotnik-core = { version = "0.2", path = "../plotnik-core" } +postcard = { version = "1", features = ["alloc"] } +serde = "1" +serde_json = "1" [dev-dependencies] diff --git a/crates/plotnik-langs/build.rs b/crates/plotnik-langs/build.rs index 13d538d4..c020b88a 100644 --- a/crates/plotnik-langs/build.rs +++ b/crates/plotnik-langs/build.rs @@ -1,10 +1,12 @@ -use std::path::PathBuf; +use std::path::{Path, PathBuf}; + +use serde::Serialize; fn main() { let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); let manifest_path = PathBuf::from(&manifest_dir).join("Cargo.toml"); + let out_dir = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR not set")); - // Collect enabled lang-* features from environment let enabled_features: Vec = std::env::vars() .filter_map(|(key, _)| { key.strip_prefix("CARGO_FEATURE_LANG_") @@ -38,20 +40,48 @@ fn main() { .parent() .expect("package has no parent dir"); + let lang_key = feature_to_lang_key(&feature_name); + + // Process grammar.json + let grammar_path = package_root.join("grammar/src/grammar.json"); + if !grammar_path.exists() { + panic!( + "grammar.json not found for {}: {}", + package.name, grammar_path + ); + } + process_json_file( + &grammar_path, + &out_dir, + &lang_key, + "GRAMMAR", + "grammar", + |json| { + plotnik_core::grammar::Grammar::from_json(json) + .expect("failed to parse grammar.json") + }, + ); + + // Process node-types.json let node_types_path = package_root.join("grammar/src/node-types.json"); - if !node_types_path.exists() { + if node_types_path.exists() { + process_json_file( + &node_types_path, + &out_dir, + &lang_key, + "NODE_TYPES", + "node_types", + |json| { + serde_json::from_str::>(json) + .expect("failed to parse node-types.json") + }, + ); + } else { panic!( "node-types.json not found for {}: {}", package.name, node_types_path ); } - - let env_var_name = format!( - "PLOTNIK_NODE_TYPES_{}", - feature_to_node_types_key(&feature_name), - ); - println!("cargo::rustc-env={}={}", env_var_name, node_types_path); - println!("cargo::rerun-if-changed={}", node_types_path); } for (key, _) in std::env::vars() { @@ -64,7 +94,40 @@ fn main() { println!("cargo::rerun-if-changed=Cargo.toml"); } -fn feature_to_node_types_key(feature: &str) -> String { +/// Parse JSON, serialize to binary, write to OUT_DIR, and set env var. +fn process_json_file( + json_path: P, + out_dir: &Path, + lang_key: &str, + env_prefix: &str, + file_suffix: &str, + parse: F, +) where + T: Serialize, + P: AsRef, + F: FnOnce(&str) -> T, +{ + let json_path = json_path.as_ref(); + let json = std::fs::read_to_string(json_path) + .unwrap_or_else(|e| panic!("failed to read {}: {}", json_path.display(), e)); + + let parsed = parse(&json); + + let binary = postcard::to_allocvec(&parsed).expect("serialization should not fail"); + let binary_path = out_dir.join(format!("{}.{}", lang_key.to_lowercase(), file_suffix)); + std::fs::write(&binary_path, &binary) + .unwrap_or_else(|e| panic!("failed to write {}: {}", binary_path.display(), e)); + + println!( + "cargo::rustc-env=PLOTNIK_{}_{}={}", + env_prefix, + lang_key, + binary_path.display() + ); + println!("cargo::rerun-if-changed={}", json_path.display()); +} + +fn feature_to_lang_key(feature: &str) -> String { match feature { "lang-c-sharp" => "C_SHARP".to_string(), "lang-ssh-config" => "SSH_CONFIG".to_string(), diff --git a/crates/plotnik-langs/src/builtin.rs b/crates/plotnik-langs/src/builtin.rs index 6fd3f464..23721bf6 100644 --- a/crates/plotnik-langs/src/builtin.rs +++ b/crates/plotnik-langs/src/builtin.rs @@ -9,28 +9,37 @@ macro_rules! define_langs { feature: $feature:literal, name: $name:literal, ts_lang: $ts_lang:expr, - node_types_key: $node_types_key:literal, + lang_key: $lang_key:literal, names: [$($alias:literal),* $(,)?], extensions: [$($ext:literal),* $(,)?] $(,)? } ),* $(,)? ) => { - // Generate NodeTypes statics via proc macro - $( - #[cfg(feature = $feature)] - plotnik_macros::generate_node_types!($node_types_key); - )* - // Generate static Lang definitions with LazyLock $( #[cfg(feature = $feature)] pub fn $fn_name() -> Lang { paste::paste! { static LANG: LazyLock = LazyLock::new(|| { - Arc::new(LangInner::new_static( + static NODE_TYPES_BYTES: &[u8] = include_bytes!(env!( + concat!("PLOTNIK_NODE_TYPES_", $lang_key) + )); + static GRAMMAR_BYTES: &[u8] = include_bytes!(env!( + concat!("PLOTNIK_GRAMMAR_", $lang_key) + )); + + let raw_nodes: Vec = + postcard::from_bytes(NODE_TYPES_BYTES) + .expect("invalid embedded node types"); + + let grammar = plotnik_core::grammar::Grammar::from_binary(GRAMMAR_BYTES) + .expect("invalid embedded grammar"); + + Arc::new(LangInner::new( $name, $ts_lang.into(), - &[<$node_types_key:upper _NODE_TYPES>], + raw_nodes, + grammar, )) }); } @@ -75,7 +84,7 @@ define_langs! { feature: "lang-ada", name: "ada", ts_lang: arborium_ada::language(), - node_types_key: "ada", + lang_key: "ADA", names: ["ada"], extensions: ["ada", "adb", "ads"], }, @@ -83,7 +92,7 @@ define_langs! { feature: "lang-agda", name: "agda", ts_lang: arborium_agda::language(), - node_types_key: "agda", + lang_key: "AGDA", names: ["agda"], extensions: ["agda"], }, @@ -91,7 +100,7 @@ define_langs! { feature: "lang-asciidoc", name: "asciidoc", ts_lang: arborium_asciidoc::language(), - node_types_key: "asciidoc", + lang_key: "ASCIIDOC", names: ["asciidoc", "adoc"], extensions: ["adoc", "asciidoc", "asc"], }, @@ -99,7 +108,7 @@ define_langs! { feature: "lang-asm", name: "asm", ts_lang: arborium_asm::language(), - node_types_key: "asm", + lang_key: "ASM", names: ["asm", "assembly"], extensions: ["asm", "s"], }, @@ -107,7 +116,7 @@ define_langs! { feature: "lang-awk", name: "awk", ts_lang: arborium_awk::language(), - node_types_key: "awk", + lang_key: "AWK", names: ["awk", "gawk", "mawk", "nawk"], extensions: ["awk"], }, @@ -115,7 +124,7 @@ define_langs! { feature: "lang-bash", name: "bash", ts_lang: arborium_bash::language(), - node_types_key: "bash", + lang_key: "BASH", names: ["bash", "sh", "shell"], extensions: ["sh", "bash"], }, @@ -123,7 +132,7 @@ define_langs! { feature: "lang-batch", name: "batch", ts_lang: arborium_batch::language(), - node_types_key: "batch", + lang_key: "BATCH", names: ["batch", "bat", "cmd"], extensions: ["bat", "cmd"], }, @@ -131,7 +140,7 @@ define_langs! { feature: "lang-c", name: "c", ts_lang: arborium_c::language(), - node_types_key: "c", + lang_key: "C", names: ["c"], extensions: ["c", "h"], }, @@ -139,7 +148,7 @@ define_langs! { feature: "lang-caddy", name: "caddy", ts_lang: arborium_caddy::language(), - node_types_key: "caddy", + lang_key: "CADDY", names: ["caddy", "caddyfile"], extensions: ["caddyfile"], }, @@ -147,7 +156,7 @@ define_langs! { feature: "lang-capnp", name: "capnp", ts_lang: arborium_capnp::language(), - node_types_key: "capnp", + lang_key: "CAPNP", names: ["capnp", "capnproto"], extensions: ["capnp"], }, @@ -155,7 +164,7 @@ define_langs! { feature: "lang-clojure", name: "clojure", ts_lang: arborium_clojure::language(), - node_types_key: "clojure", + lang_key: "CLOJURE", names: ["clojure", "clj"], extensions: ["clj", "cljs", "cljc", "edn"], }, @@ -163,7 +172,7 @@ define_langs! { feature: "lang-cmake", name: "cmake", ts_lang: arborium_cmake::language(), - node_types_key: "cmake", + lang_key: "CMAKE", names: ["cmake"], extensions: ["cmake"], }, @@ -171,7 +180,7 @@ define_langs! { feature: "lang-commonlisp", name: "commonlisp", ts_lang: arborium_commonlisp::language(), - node_types_key: "commonlisp", + lang_key: "COMMONLISP", names: ["commonlisp", "common-lisp", "lisp", "cl"], extensions: ["lisp", "lsp", "cl"], }, @@ -179,7 +188,7 @@ define_langs! { feature: "lang-cpp", name: "cpp", ts_lang: arborium_cpp::language(), - node_types_key: "cpp", + lang_key: "CPP", names: ["cpp", "c++", "cxx", "cc"], extensions: ["cpp", "cc", "cxx", "hpp", "hh", "hxx", "h++", "c++"], }, @@ -187,7 +196,7 @@ define_langs! { feature: "lang-c-sharp", name: "c_sharp", ts_lang: arborium_c_sharp::language(), - node_types_key: "c_sharp", + lang_key: "C_SHARP", names: ["csharp", "c#", "cs", "c_sharp"], extensions: ["cs"], }, @@ -195,7 +204,7 @@ define_langs! { feature: "lang-css", name: "css", ts_lang: arborium_css::language(), - node_types_key: "css", + lang_key: "CSS", names: ["css"], extensions: ["css"], }, @@ -203,7 +212,7 @@ define_langs! { feature: "lang-d", name: "d", ts_lang: arborium_d::language(), - node_types_key: "d", + lang_key: "D", names: ["d", "dlang"], extensions: ["d"], }, @@ -211,7 +220,7 @@ define_langs! { feature: "lang-dart", name: "dart", ts_lang: arborium_dart::language(), - node_types_key: "dart", + lang_key: "DART", names: ["dart"], extensions: ["dart"], }, @@ -219,7 +228,7 @@ define_langs! { feature: "lang-devicetree", name: "devicetree", ts_lang: arborium_devicetree::language(), - node_types_key: "devicetree", + lang_key: "DEVICETREE", names: ["devicetree", "dts"], extensions: ["dts", "dtsi"], }, @@ -227,7 +236,7 @@ define_langs! { feature: "lang-diff", name: "diff", ts_lang: arborium_diff::language(), - node_types_key: "diff", + lang_key: "DIFF", names: ["diff", "patch"], extensions: ["diff", "patch"], }, @@ -235,7 +244,7 @@ define_langs! { feature: "lang-dockerfile", name: "dockerfile", ts_lang: arborium_dockerfile::language(), - node_types_key: "dockerfile", + lang_key: "DOCKERFILE", names: ["dockerfile", "docker"], extensions: ["dockerfile"], }, @@ -243,7 +252,7 @@ define_langs! { feature: "lang-dot", name: "dot", ts_lang: arborium_dot::language(), - node_types_key: "dot", + lang_key: "DOT", names: ["dot", "graphviz"], extensions: ["dot", "gv"], }, @@ -251,7 +260,7 @@ define_langs! { feature: "lang-elisp", name: "elisp", ts_lang: arborium_elisp::language(), - node_types_key: "elisp", + lang_key: "ELISP", names: ["elisp", "emacs-lisp"], extensions: ["el"], }, @@ -259,7 +268,7 @@ define_langs! { feature: "lang-elixir", name: "elixir", ts_lang: arborium_elixir::language(), - node_types_key: "elixir", + lang_key: "ELIXIR", names: ["elixir", "ex"], extensions: ["ex", "exs"], }, @@ -267,7 +276,7 @@ define_langs! { feature: "lang-elm", name: "elm", ts_lang: arborium_elm::language(), - node_types_key: "elm", + lang_key: "ELM", names: ["elm"], extensions: ["elm"], }, @@ -275,7 +284,7 @@ define_langs! { feature: "lang-erlang", name: "erlang", ts_lang: arborium_erlang::language(), - node_types_key: "erlang", + lang_key: "ERLANG", names: ["erlang", "erl"], extensions: ["erl", "hrl"], }, @@ -283,7 +292,7 @@ define_langs! { feature: "lang-fish", name: "fish", ts_lang: arborium_fish::language(), - node_types_key: "fish", + lang_key: "FISH", names: ["fish"], extensions: ["fish"], }, @@ -291,7 +300,7 @@ define_langs! { feature: "lang-fsharp", name: "fsharp", ts_lang: arborium_fsharp::language(), - node_types_key: "fsharp", + lang_key: "FSHARP", names: ["fsharp", "f#", "fs"], extensions: ["fs", "fsi", "fsx"], }, @@ -299,7 +308,7 @@ define_langs! { feature: "lang-gleam", name: "gleam", ts_lang: arborium_gleam::language(), - node_types_key: "gleam", + lang_key: "GLEAM", names: ["gleam"], extensions: ["gleam"], }, @@ -307,7 +316,7 @@ define_langs! { feature: "lang-glsl", name: "glsl", ts_lang: arborium_glsl::language(), - node_types_key: "glsl", + lang_key: "GLSL", names: ["glsl"], extensions: ["glsl", "vert", "frag", "geom", "tesc", "tese", "comp"], }, @@ -315,7 +324,7 @@ define_langs! { feature: "lang-go", name: "go", ts_lang: arborium_go::language(), - node_types_key: "go", + lang_key: "GO", names: ["go", "golang"], extensions: ["go"], }, @@ -323,7 +332,7 @@ define_langs! { feature: "lang-graphql", name: "graphql", ts_lang: arborium_graphql::language(), - node_types_key: "graphql", + lang_key: "GRAPHQL", names: ["graphql", "gql"], extensions: ["graphql", "gql"], }, @@ -331,7 +340,7 @@ define_langs! { feature: "lang-groovy", name: "groovy", ts_lang: arborium_groovy::language(), - node_types_key: "groovy", + lang_key: "GROOVY", names: ["groovy", "gradle"], extensions: ["groovy", "gradle", "gvy", "gy", "gsh"], }, @@ -339,7 +348,7 @@ define_langs! { feature: "lang-haskell", name: "haskell", ts_lang: arborium_haskell::language(), - node_types_key: "haskell", + lang_key: "HASKELL", names: ["haskell", "hs"], extensions: ["hs", "lhs"], }, @@ -347,7 +356,7 @@ define_langs! { feature: "lang-hcl", name: "hcl", ts_lang: arborium_hcl::language(), - node_types_key: "hcl", + lang_key: "HCL", names: ["hcl", "terraform", "tf"], extensions: ["hcl", "tf", "tfvars"], }, @@ -355,7 +364,7 @@ define_langs! { feature: "lang-hlsl", name: "hlsl", ts_lang: arborium_hlsl::language(), - node_types_key: "hlsl", + lang_key: "HLSL", names: ["hlsl"], extensions: ["hlsl", "hlsli", "fx"], }, @@ -363,7 +372,7 @@ define_langs! { feature: "lang-html", name: "html", ts_lang: arborium_html::language(), - node_types_key: "html", + lang_key: "HTML", names: ["html", "htm"], extensions: ["html", "htm"], }, @@ -371,7 +380,7 @@ define_langs! { feature: "lang-idris", name: "idris", ts_lang: arborium_idris::language(), - node_types_key: "idris", + lang_key: "IDRIS", names: ["idris"], extensions: ["idr"], }, @@ -379,7 +388,7 @@ define_langs! { feature: "lang-ini", name: "ini", ts_lang: arborium_ini::language(), - node_types_key: "ini", + lang_key: "INI", names: ["ini"], extensions: ["ini", "cfg", "conf"], }, @@ -387,7 +396,7 @@ define_langs! { feature: "lang-java", name: "java", ts_lang: arborium_java::language(), - node_types_key: "java", + lang_key: "JAVA", names: ["java"], extensions: ["java"], }, @@ -395,7 +404,7 @@ define_langs! { feature: "lang-javascript", name: "javascript", ts_lang: arborium_javascript::language(), - node_types_key: "javascript", + lang_key: "JAVASCRIPT", names: ["javascript", "js", "jsx", "ecmascript", "es"], extensions: ["js", "mjs", "cjs", "jsx"], }, @@ -403,7 +412,7 @@ define_langs! { feature: "lang-jinja2", name: "jinja2", ts_lang: arborium_jinja2::language(), - node_types_key: "jinja2", + lang_key: "JINJA2", names: ["jinja2", "jinja"], extensions: ["j2", "jinja", "jinja2"], }, @@ -411,7 +420,7 @@ define_langs! { feature: "lang-jq", name: "jq", ts_lang: arborium_jq::language(), - node_types_key: "jq", + lang_key: "JQ", names: ["jq"], extensions: ["jq"], }, @@ -419,7 +428,7 @@ define_langs! { feature: "lang-json", name: "json", ts_lang: arborium_json::language(), - node_types_key: "json", + lang_key: "JSON", names: ["json"], extensions: ["json"], }, @@ -427,7 +436,7 @@ define_langs! { feature: "lang-julia", name: "julia", ts_lang: arborium_julia::language(), - node_types_key: "julia", + lang_key: "JULIA", names: ["julia", "jl"], extensions: ["jl"], }, @@ -435,7 +444,7 @@ define_langs! { feature: "lang-kdl", name: "kdl", ts_lang: arborium_kdl::language(), - node_types_key: "kdl", + lang_key: "KDL", names: ["kdl"], extensions: ["kdl"], }, @@ -443,7 +452,7 @@ define_langs! { feature: "lang-kotlin", name: "kotlin", ts_lang: arborium_kotlin::language(), - node_types_key: "kotlin", + lang_key: "KOTLIN", names: ["kotlin", "kt"], extensions: ["kt", "kts"], }, @@ -451,7 +460,7 @@ define_langs! { feature: "lang-lean", name: "lean", ts_lang: arborium_lean::language(), - node_types_key: "lean", + lang_key: "LEAN", names: ["lean", "lean4"], extensions: ["lean"], }, @@ -459,7 +468,7 @@ define_langs! { feature: "lang-lua", name: "lua", ts_lang: arborium_lua::language(), - node_types_key: "lua", + lang_key: "LUA", names: ["lua"], extensions: ["lua"], }, @@ -467,7 +476,7 @@ define_langs! { feature: "lang-markdown", name: "markdown", ts_lang: arborium_markdown::language(), - node_types_key: "markdown", + lang_key: "MARKDOWN", names: ["markdown", "md"], extensions: ["md", "markdown"], }, @@ -475,7 +484,7 @@ define_langs! { feature: "lang-matlab", name: "matlab", ts_lang: arborium_matlab::language(), - node_types_key: "matlab", + lang_key: "MATLAB", names: ["matlab", "octave"], extensions: ["m"], }, @@ -483,7 +492,7 @@ define_langs! { feature: "lang-meson", name: "meson", ts_lang: arborium_meson::language(), - node_types_key: "meson", + lang_key: "MESON", names: ["meson"], extensions: ["meson"], }, @@ -491,7 +500,7 @@ define_langs! { feature: "lang-nginx", name: "nginx", ts_lang: arborium_nginx::language(), - node_types_key: "nginx", + lang_key: "NGINX", names: ["nginx"], extensions: ["nginx"], }, @@ -499,7 +508,7 @@ define_langs! { feature: "lang-ninja", name: "ninja", ts_lang: arborium_ninja::language(), - node_types_key: "ninja", + lang_key: "NINJA", names: ["ninja"], extensions: ["ninja"], }, @@ -507,7 +516,7 @@ define_langs! { feature: "lang-nix", name: "nix", ts_lang: arborium_nix::language(), - node_types_key: "nix", + lang_key: "NIX", names: ["nix"], extensions: ["nix"], }, @@ -515,7 +524,7 @@ define_langs! { feature: "lang-objc", name: "objc", ts_lang: arborium_objc::language(), - node_types_key: "objc", + lang_key: "OBJC", names: ["objc", "objective-c", "objectivec"], extensions: ["m", "mm"], }, @@ -523,7 +532,7 @@ define_langs! { feature: "lang-ocaml", name: "ocaml", ts_lang: arborium_ocaml::language(), - node_types_key: "ocaml", + lang_key: "OCAML", names: ["ocaml", "ml"], extensions: ["ml", "mli"], }, @@ -531,7 +540,7 @@ define_langs! { feature: "lang-perl", name: "perl", ts_lang: arborium_perl::language(), - node_types_key: "perl", + lang_key: "PERL", names: ["perl", "pl"], extensions: ["pl", "pm"], }, @@ -539,7 +548,7 @@ define_langs! { feature: "lang-php", name: "php", ts_lang: arborium_php::language(), - node_types_key: "php", + lang_key: "PHP", names: ["php"], extensions: ["php"], }, @@ -547,7 +556,7 @@ define_langs! { feature: "lang-postscript", name: "postscript", ts_lang: arborium_postscript::language(), - node_types_key: "postscript", + lang_key: "POSTSCRIPT", names: ["postscript", "ps"], extensions: ["ps", "eps"], }, @@ -555,7 +564,7 @@ define_langs! { feature: "lang-powershell", name: "powershell", ts_lang: arborium_powershell::language(), - node_types_key: "powershell", + lang_key: "POWERSHELL", names: ["powershell", "pwsh", "ps1"], extensions: ["ps1", "psm1", "psd1"], }, @@ -563,7 +572,7 @@ define_langs! { feature: "lang-prolog", name: "prolog", ts_lang: arborium_prolog::language(), - node_types_key: "prolog", + lang_key: "PROLOG", names: ["prolog"], extensions: ["pl", "pro"], }, @@ -571,7 +580,7 @@ define_langs! { feature: "lang-python", name: "python", ts_lang: arborium_python::language(), - node_types_key: "python", + lang_key: "PYTHON", names: ["python", "py"], extensions: ["py", "pyi", "pyw"], }, @@ -579,7 +588,7 @@ define_langs! { feature: "lang-query", name: "query", ts_lang: arborium_query::language(), - node_types_key: "query", + lang_key: "QUERY", names: ["query", "scm"], extensions: ["scm"], }, @@ -587,7 +596,7 @@ define_langs! { feature: "lang-r", name: "r", ts_lang: arborium_r::language(), - node_types_key: "r", + lang_key: "R", names: ["r", "rlang"], extensions: ["r", "R"], }, @@ -595,7 +604,7 @@ define_langs! { feature: "lang-rescript", name: "rescript", ts_lang: arborium_rescript::language(), - node_types_key: "rescript", + lang_key: "RESCRIPT", names: ["rescript", "res"], extensions: ["res", "resi"], }, @@ -603,7 +612,7 @@ define_langs! { feature: "lang-ron", name: "ron", ts_lang: arborium_ron::language(), - node_types_key: "ron", + lang_key: "RON", names: ["ron"], extensions: ["ron"], }, @@ -611,7 +620,7 @@ define_langs! { feature: "lang-ruby", name: "ruby", ts_lang: arborium_ruby::language(), - node_types_key: "ruby", + lang_key: "RUBY", names: ["ruby", "rb"], extensions: ["rb", "rake", "gemspec"], }, @@ -619,7 +628,7 @@ define_langs! { feature: "lang-rust", name: "rust", ts_lang: arborium_rust::language(), - node_types_key: "rust", + lang_key: "RUST", names: ["rust", "rs"], extensions: ["rs"], }, @@ -627,7 +636,7 @@ define_langs! { feature: "lang-scala", name: "scala", ts_lang: arborium_scala::language(), - node_types_key: "scala", + lang_key: "SCALA", names: ["scala"], extensions: ["scala", "sc"], }, @@ -635,7 +644,7 @@ define_langs! { feature: "lang-scheme", name: "scheme", ts_lang: arborium_scheme::language(), - node_types_key: "scheme", + lang_key: "SCHEME", names: ["scheme", "racket"], extensions: ["scm", "ss", "rkt"], }, @@ -643,7 +652,7 @@ define_langs! { feature: "lang-scss", name: "scss", ts_lang: arborium_scss::language(), - node_types_key: "scss", + lang_key: "SCSS", names: ["scss", "sass"], extensions: ["scss", "sass"], }, @@ -651,7 +660,7 @@ define_langs! { feature: "lang-sparql", name: "sparql", ts_lang: arborium_sparql::language(), - node_types_key: "sparql", + lang_key: "SPARQL", names: ["sparql"], extensions: ["sparql", "rq"], }, @@ -659,7 +668,7 @@ define_langs! { feature: "lang-sql", name: "sql", ts_lang: arborium_sql::language(), - node_types_key: "sql", + lang_key: "SQL", names: ["sql"], extensions: ["sql"], }, @@ -667,7 +676,7 @@ define_langs! { feature: "lang-ssh-config", name: "ssh_config", ts_lang: arborium_ssh_config::language(), - node_types_key: "ssh_config", + lang_key: "SSH_CONFIG", names: ["ssh-config", "ssh_config", "sshconfig"], extensions: ["ssh_config"], }, @@ -675,7 +684,7 @@ define_langs! { feature: "lang-starlark", name: "starlark", ts_lang: arborium_starlark::language(), - node_types_key: "starlark", + lang_key: "STARLARK", names: ["starlark", "bazel", "bzl"], extensions: ["bzl", "bazel"], }, @@ -683,7 +692,7 @@ define_langs! { feature: "lang-svelte", name: "svelte", ts_lang: arborium_svelte::language(), - node_types_key: "svelte", + lang_key: "SVELTE", names: ["svelte"], extensions: ["svelte"], }, @@ -691,7 +700,7 @@ define_langs! { feature: "lang-swift", name: "swift", ts_lang: arborium_swift::language(), - node_types_key: "swift", + lang_key: "SWIFT", names: ["swift"], extensions: ["swift"], }, @@ -699,7 +708,7 @@ define_langs! { feature: "lang-textproto", name: "textproto", ts_lang: arborium_textproto::language(), - node_types_key: "textproto", + lang_key: "TEXTPROTO", names: ["textproto", "pbtxt"], extensions: ["textproto", "pbtxt"], }, @@ -707,7 +716,7 @@ define_langs! { feature: "lang-thrift", name: "thrift", ts_lang: arborium_thrift::language(), - node_types_key: "thrift", + lang_key: "THRIFT", names: ["thrift"], extensions: ["thrift"], }, @@ -715,7 +724,7 @@ define_langs! { feature: "lang-tlaplus", name: "tlaplus", ts_lang: arborium_tlaplus::language(), - node_types_key: "tlaplus", + lang_key: "TLAPLUS", names: ["tlaplus", "tla+", "tla"], extensions: ["tla"], }, @@ -723,7 +732,7 @@ define_langs! { feature: "lang-toml", name: "toml", ts_lang: arborium_toml::language(), - node_types_key: "toml", + lang_key: "TOML", names: ["toml"], extensions: ["toml"], }, @@ -731,7 +740,7 @@ define_langs! { feature: "lang-tsx", name: "tsx", ts_lang: arborium_tsx::language(), - node_types_key: "tsx", + lang_key: "TSX", names: ["tsx"], extensions: ["tsx"], }, @@ -739,7 +748,7 @@ define_langs! { feature: "lang-typescript", name: "typescript", ts_lang: arborium_typescript::language(), - node_types_key: "typescript", + lang_key: "TYPESCRIPT", names: ["typescript", "ts"], extensions: ["ts", "mts", "cts"], }, @@ -747,7 +756,7 @@ define_langs! { feature: "lang-typst", name: "typst", ts_lang: arborium_typst::language(), - node_types_key: "typst", + lang_key: "TYPST", names: ["typst"], extensions: ["typ"], }, @@ -755,7 +764,7 @@ define_langs! { feature: "lang-uiua", name: "uiua", ts_lang: arborium_uiua::language(), - node_types_key: "uiua", + lang_key: "UIUA", names: ["uiua"], extensions: ["ua"], }, @@ -763,7 +772,7 @@ define_langs! { feature: "lang-vb", name: "vb", ts_lang: arborium_vb::language(), - node_types_key: "vb", + lang_key: "VB", names: ["vb", "vbnet", "visualbasic"], extensions: ["vb"], }, @@ -771,7 +780,7 @@ define_langs! { feature: "lang-verilog", name: "verilog", ts_lang: arborium_verilog::language(), - node_types_key: "verilog", + lang_key: "VERILOG", names: ["verilog", "v"], extensions: ["v", "sv"], }, @@ -779,7 +788,7 @@ define_langs! { feature: "lang-vhdl", name: "vhdl", ts_lang: arborium_vhdl::language(), - node_types_key: "vhdl", + lang_key: "VHDL", names: ["vhdl"], extensions: ["vhd", "vhdl"], }, @@ -787,7 +796,7 @@ define_langs! { feature: "lang-vim", name: "vim", ts_lang: arborium_vim::language(), - node_types_key: "vim", + lang_key: "VIM", names: ["vim", "vimscript"], extensions: ["vim"], }, @@ -795,7 +804,7 @@ define_langs! { feature: "lang-vue", name: "vue", ts_lang: arborium_vue::language(), - node_types_key: "vue", + lang_key: "VUE", names: ["vue"], extensions: ["vue"], }, @@ -803,7 +812,7 @@ define_langs! { feature: "lang-wit", name: "wit", ts_lang: arborium_wit::language(), - node_types_key: "wit", + lang_key: "WIT", names: ["wit"], extensions: ["wit"], }, @@ -811,7 +820,7 @@ define_langs! { feature: "lang-x86asm", name: "x86asm", ts_lang: arborium_x86asm::language(), - node_types_key: "x86asm", + lang_key: "X86ASM", names: ["x86asm", "x86"], extensions: ["asm"], }, @@ -819,7 +828,7 @@ define_langs! { feature: "lang-xml", name: "xml", ts_lang: arborium_xml::language(), - node_types_key: "xml", + lang_key: "XML", names: ["xml"], extensions: ["xml", "xsl", "xslt", "xsd", "svg"], }, @@ -827,7 +836,7 @@ define_langs! { feature: "lang-yaml", name: "yaml", ts_lang: arborium_yaml::language(), - node_types_key: "yaml", + lang_key: "YAML", names: ["yaml", "yml"], extensions: ["yaml", "yml"], }, @@ -835,7 +844,7 @@ define_langs! { feature: "lang-yuri", name: "yuri", ts_lang: arborium_yuri::language(), - node_types_key: "yuri", + lang_key: "YURI", names: ["yuri"], extensions: ["yuri"], }, @@ -843,7 +852,7 @@ define_langs! { feature: "lang-zig", name: "zig", ts_lang: arborium_zig::language(), - node_types_key: "zig", + lang_key: "ZIG", names: ["zig"], extensions: ["zig"], }, @@ -851,7 +860,7 @@ define_langs! { feature: "lang-zsh", name: "zsh", ts_lang: arborium_zsh::language(), - node_types_key: "zsh", + lang_key: "ZSH", names: ["zsh"], extensions: ["zsh"], }, diff --git a/crates/plotnik-langs/src/lib.rs b/crates/plotnik-langs/src/lib.rs index 3a697dbe..70bd4e7f 100644 --- a/crates/plotnik-langs/src/lib.rs +++ b/crates/plotnik-langs/src/lib.rs @@ -3,7 +3,8 @@ use std::sync::Arc; use arborium_tree_sitter::Language; -use plotnik_core::{Cardinality, NodeFieldId, NodeTypeId, NodeTypes, StaticNodeTypes}; +use plotnik_core::grammar::Grammar; +use plotnik_core::{Cardinality, DynamicNodeTypes, NodeFieldId, NodeTypeId, NodeTypes, RawNode}; pub mod builtin; pub mod dynamic; @@ -62,34 +63,46 @@ pub trait LangImpl: Send + Sync { fn children_cardinality(&self, node_type_id: NodeTypeId) -> Option; fn valid_child_types(&self, node_type_id: NodeTypeId) -> &[NodeTypeId]; fn is_valid_child_type(&self, node_type_id: NodeTypeId, child: NodeTypeId) -> bool; + + /// Get the grammar for this language (for grammar-verify). + fn grammar(&self) -> &Grammar; } -/// Generic language implementation parameterized by node types. -/// -/// This struct provides a single implementation of `LangImpl` that works with -/// any `NodeTypes` implementation (static or dynamic). +/// Language implementation with embedded grammar and node types. #[derive(Debug)] -pub struct LangInner { +pub struct LangInner { name: String, ts_lang: Language, - node_types: N, + node_types: DynamicNodeTypes, + grammar: Grammar, } -impl LangInner<&'static StaticNodeTypes> { - pub fn new_static(name: &str, ts_lang: Language, node_types: &'static StaticNodeTypes) -> Self { +impl LangInner { + /// Create a new language from raw node types and grammar. + pub fn new(name: &str, ts_lang: Language, raw_nodes: Vec, grammar: Grammar) -> Self { + let node_types = DynamicNodeTypes::build( + &raw_nodes, + |kind, named| { + let id = ts_lang.id_for_node_kind(kind, named); + NonZeroU16::new(id) + }, + |field_name| ts_lang.field_id_for_name(field_name), + ); + Self { name: name.to_owned(), ts_lang, node_types, + grammar, } } - pub fn node_types(&self) -> &'static StaticNodeTypes { - self.node_types + pub fn node_types(&self) -> &DynamicNodeTypes { + &self.node_types } } -impl LangImpl for LangInner { +impl LangImpl for LangInner { fn name(&self) -> &str { &self.name } @@ -213,4 +226,8 @@ impl LangImpl for LangInner { fn is_valid_child_type(&self, node_type_id: NodeTypeId, child: NodeTypeId) -> bool { self.node_types.is_valid_child_type(node_type_id, child) } + + fn grammar(&self) -> &Grammar { + &self.grammar + } } diff --git a/crates/plotnik-macros/Cargo.toml b/crates/plotnik-macros/Cargo.toml deleted file mode 100644 index 01eb60e5..00000000 --- a/crates/plotnik-macros/Cargo.toml +++ /dev/null @@ -1,217 +0,0 @@ -[package] -name = "plotnik-macros" -version = "0.2.0" -edition = "2024" -license = "Apache-2.0" -description = "Procedural macros for Plotnik" -repository = "https://github.com/plotnik-lang/plotnik" - -[lib] -proc-macro = true - -[features] -default = [] -lang-ada = ["dep:arborium-ada"] -lang-agda = ["dep:arborium-agda"] -lang-asciidoc = ["dep:arborium-asciidoc"] -lang-asm = ["dep:arborium-asm"] -lang-awk = ["dep:arborium-awk"] -lang-bash = ["dep:arborium-bash"] -lang-batch = ["dep:arborium-batch"] -lang-c = ["dep:arborium-c"] -lang-c-sharp = ["dep:arborium-c-sharp"] -lang-caddy = ["dep:arborium-caddy"] -lang-capnp = ["dep:arborium-capnp"] -lang-clojure = ["dep:arborium-clojure"] -lang-cmake = ["dep:arborium-cmake"] -lang-commonlisp = ["dep:arborium-commonlisp"] -lang-cpp = ["dep:arborium-cpp"] -lang-css = ["dep:arborium-css"] -lang-d = ["dep:arborium-d"] -lang-dart = ["dep:arborium-dart"] -lang-devicetree = ["dep:arborium-devicetree"] -lang-diff = ["dep:arborium-diff"] -lang-dockerfile = ["dep:arborium-dockerfile"] -lang-dot = ["dep:arborium-dot"] -lang-elisp = ["dep:arborium-elisp"] -lang-elixir = ["dep:arborium-elixir"] -lang-elm = ["dep:arborium-elm"] -lang-erlang = ["dep:arborium-erlang"] -lang-fish = ["dep:arborium-fish"] -lang-fsharp = ["dep:arborium-fsharp"] -lang-gleam = ["dep:arborium-gleam"] -lang-glsl = ["dep:arborium-glsl"] -lang-go = ["dep:arborium-go"] -lang-graphql = ["dep:arborium-graphql"] -lang-groovy = ["dep:arborium-groovy"] -lang-haskell = ["dep:arborium-haskell"] -lang-hcl = ["dep:arborium-hcl"] -lang-hlsl = ["dep:arborium-hlsl"] -lang-html = ["dep:arborium-html"] -lang-idris = ["dep:arborium-idris"] -lang-ini = ["dep:arborium-ini"] -lang-java = ["dep:arborium-java"] -lang-javascript = ["dep:arborium-javascript"] -lang-jinja2 = ["dep:arborium-jinja2"] -lang-jq = ["dep:arborium-jq"] -lang-json = ["dep:arborium-json"] -lang-julia = ["dep:arborium-julia"] -lang-kdl = ["dep:arborium-kdl"] -lang-kotlin = ["dep:arborium-kotlin"] -lang-lean = ["dep:arborium-lean"] -lang-lua = ["dep:arborium-lua"] -lang-markdown = ["dep:arborium-markdown"] -lang-matlab = ["dep:arborium-matlab"] -lang-meson = ["dep:arborium-meson"] -lang-nginx = ["dep:arborium-nginx"] -lang-ninja = ["dep:arborium-ninja"] -lang-nix = ["dep:arborium-nix"] -lang-objc = ["dep:arborium-objc"] -lang-ocaml = ["dep:arborium-ocaml"] -lang-perl = ["dep:arborium-perl"] -lang-php = ["dep:arborium-php"] -lang-postscript = ["dep:arborium-postscript"] -lang-powershell = ["dep:arborium-powershell"] -lang-prolog = ["dep:arborium-prolog"] -lang-python = ["dep:arborium-python"] -lang-query = ["dep:arborium-query"] -lang-r = ["dep:arborium-r"] -lang-rescript = ["dep:arborium-rescript"] -lang-ron = ["dep:arborium-ron"] -lang-ruby = ["dep:arborium-ruby"] -lang-rust = ["dep:arborium-rust"] -lang-scala = ["dep:arborium-scala"] -lang-scheme = ["dep:arborium-scheme"] -lang-scss = ["dep:arborium-scss"] -lang-sparql = ["dep:arborium-sparql"] -lang-sql = ["dep:arborium-sql"] -lang-ssh-config = ["dep:arborium-ssh-config"] -lang-starlark = ["dep:arborium-starlark"] -lang-svelte = ["dep:arborium-svelte"] -lang-swift = ["dep:arborium-swift"] -lang-textproto = ["dep:arborium-textproto"] -lang-thrift = ["dep:arborium-thrift"] -lang-tlaplus = ["dep:arborium-tlaplus"] -lang-toml = ["dep:arborium-toml"] -lang-tsx = ["dep:arborium-tsx"] -lang-typescript = ["dep:arborium-typescript"] -lang-typst = ["dep:arborium-typst"] -lang-uiua = ["dep:arborium-uiua"] -lang-vb = ["dep:arborium-vb"] -lang-verilog = ["dep:arborium-verilog"] -lang-vhdl = ["dep:arborium-vhdl"] -lang-vim = ["dep:arborium-vim"] -lang-vue = ["dep:arborium-vue"] -lang-wit = ["dep:arborium-wit"] -lang-x86asm = ["dep:arborium-x86asm"] -lang-xml = ["dep:arborium-xml"] -lang-yaml = ["dep:arborium-yaml"] -lang-yuri = ["dep:arborium-yuri"] -lang-zig = ["dep:arborium-zig"] -lang-zsh = ["dep:arborium-zsh"] - -[dependencies] -proc-macro2 = "1" -quote = "1" -syn = "2" -plotnik-core = { version = "0.2", path = "../plotnik-core" } -serde_json = "1" -arborium-tree-sitter = "2.5.0" -arborium-ada = { version = "2.5.0", optional = true } -arborium-agda = { version = "2.5.0", optional = true } -arborium-asciidoc = { version = "2.5.0", optional = true } -arborium-asm = { version = "2.5.0", optional = true } -arborium-awk = { version = "2.5.0", optional = true } -arborium-bash = { version = "2.5.0", optional = true } -arborium-batch = { version = "2.5.0", optional = true } -arborium-c = { version = "2.5.0", optional = true } -arborium-c-sharp = { version = "2.5.0", optional = true } -arborium-caddy = { version = "2.5.0", optional = true } -arborium-capnp = { version = "2.5.0", optional = true } -arborium-clojure = { version = "2.5.0", optional = true } -arborium-cmake = { version = "2.5.0", optional = true } -arborium-commonlisp = { version = "2.5.0", optional = true } -arborium-cpp = { version = "2.5.0", optional = true } -arborium-css = { version = "2.5.0", optional = true } -arborium-d = { version = "2.5.0", optional = true } -arborium-dart = { version = "2.5.0", optional = true } -arborium-devicetree = { version = "2.5.0", optional = true } -arborium-diff = { version = "2.5.0", optional = true } -arborium-dockerfile = { version = "2.5.0", optional = true } -arborium-dot = { version = "2.5.0", optional = true } -arborium-elisp = { version = "2.5.0", optional = true } -arborium-elixir = { version = "2.5.0", optional = true } -arborium-elm = { version = "2.5.0", optional = true } -arborium-erlang = { version = "2.5.0", optional = true } -arborium-fish = { version = "2.5.0", optional = true } -arborium-fsharp = { version = "2.5.0", optional = true } -arborium-gleam = { version = "2.5.0", optional = true } -arborium-glsl = { version = "2.5.0", optional = true } -arborium-go = { version = "2.5.0", optional = true } -arborium-graphql = { version = "2.5.0", optional = true } -arborium-groovy = { version = "2.5.0", optional = true } -arborium-haskell = { version = "2.5.0", optional = true } -arborium-hcl = { version = "2.5.0", optional = true } -arborium-hlsl = { version = "2.5.0", optional = true } -arborium-html = { version = "2.5.0", optional = true } -arborium-idris = { version = "2.5.0", optional = true } -arborium-ini = { version = "2.5.0", optional = true } -arborium-java = { version = "2.5.0", optional = true } -arborium-javascript = { version = "2.5.0", optional = true } -arborium-jinja2 = { version = "2.5.0", optional = true } -arborium-jq = { version = "2.5.0", optional = true } -arborium-json = { version = "2.5.0", optional = true } -arborium-julia = { version = "2.5.0", optional = true } -arborium-kdl = { version = "2.5.0", optional = true } -arborium-kotlin = { version = "2.5.0", optional = true } -arborium-lean = { version = "2.5.0", optional = true } -arborium-lua = { version = "2.5.0", optional = true } -arborium-markdown = { version = "2.5.0", optional = true } -arborium-matlab = { version = "2.5.0", optional = true } -arborium-meson = { version = "2.5.0", optional = true } -arborium-nginx = { version = "2.5.0", optional = true } -arborium-ninja = { version = "2.5.0", optional = true } -arborium-nix = { version = "2.5.0", optional = true } -arborium-objc = { version = "2.5.0", optional = true } -arborium-ocaml = { version = "2.5.0", optional = true } -arborium-perl = { version = "2.5.0", optional = true } -arborium-php = { version = "2.5.0", optional = true } -arborium-postscript = { version = "2.5.0", optional = true } -arborium-powershell = { version = "2.5.0", optional = true } -arborium-prolog = { version = "2.5.0", optional = true } -arborium-python = { version = "2.5.0", optional = true } -arborium-query = { version = "2.5.0", optional = true } -arborium-r = { version = "2.5.0", optional = true } -arborium-rescript = { version = "2.5.0", optional = true } -arborium-ron = { version = "2.5.0", optional = true } -arborium-ruby = { version = "2.5.0", optional = true } -arborium-rust = { version = "2.5.0", optional = true } -arborium-scala = { version = "2.5.0", optional = true } -arborium-scheme = { version = "2.5.0", optional = true } -arborium-scss = { version = "2.5.0", optional = true } -arborium-sparql = { version = "2.5.0", optional = true } -arborium-sql = { version = "2.5.0", optional = true } -arborium-ssh-config = { version = "2.5.0", optional = true } -arborium-starlark = { version = "2.5.0", optional = true } -arborium-svelte = { version = "2.5.0", optional = true } -arborium-swift = { version = "2.5.0", optional = true } -arborium-textproto = { version = "2.5.0", optional = true } -arborium-thrift = { version = "2.5.0", optional = true } -arborium-tlaplus = { version = "2.5.0", optional = true } -arborium-toml = { version = "2.5.0", optional = true } -arborium-tsx = { version = "2.5.0", optional = true } -arborium-typescript = { version = "2.5.0", optional = true } -arborium-typst = { version = "2.5.0", optional = true } -arborium-uiua = { version = "2.5.0", optional = true } -arborium-vb = { version = "2.5.0", optional = true } -arborium-verilog = { version = "2.5.0", optional = true } -arborium-vhdl = { version = "2.5.0", optional = true } -arborium-vim = { version = "2.5.0", optional = true } -arborium-vue = { version = "2.5.0", optional = true } -arborium-wit = { version = "2.5.0", optional = true } -arborium-x86asm = { version = "2.5.0", optional = true } -arborium-xml = { version = "2.5.0", optional = true } -arborium-yaml = { version = "2.5.0", optional = true } -arborium-yuri = { version = "2.5.0", optional = true } -arborium-zig = { version = "2.5.0", optional = true } -arborium-zsh = { version = "2.5.0", optional = true } \ No newline at end of file diff --git a/crates/plotnik-macros/src/lib.rs b/crates/plotnik-macros/src/lib.rs deleted file mode 100644 index e680c8a1..00000000 --- a/crates/plotnik-macros/src/lib.rs +++ /dev/null @@ -1,449 +0,0 @@ -use arborium_tree_sitter as tree_sitter; -use proc_macro::TokenStream; -use proc_macro2::Span; -use quote::quote; -use syn::{LitStr, parse_macro_input}; -use tree_sitter::Language; - -use plotnik_core::NodeTypes; - -/// Generate a StaticNodeTypes constant for a language. -/// -/// Usage: `generate_node_types!("javascript")` -/// -/// This reads the node-types.json at compile time and uses the tree-sitter -/// Language to resolve node/field names to IDs, producing efficient lookup tables. -/// The output is fully statically allocated - no runtime initialization needed. -#[proc_macro] -pub fn generate_node_types(input: TokenStream) -> TokenStream { - let lang_key = parse_macro_input!(input as LitStr).value(); - - let env_var = format!("PLOTNIK_NODE_TYPES_{}", lang_key.to_uppercase()); - - let json_path = std::env::var(&env_var).unwrap_or_else(|_| { - panic!( - "Environment variable {} not set. Is build.rs configured correctly?", - env_var - ) - }); - - let json_content = std::fs::read_to_string(&json_path) - .unwrap_or_else(|e| panic!("Failed to read {}: {}", json_path, e)); - - let raw_nodes: Vec = serde_json::from_str(&json_content) - .unwrap_or_else(|e| panic!("Failed to parse {}: {}", json_path, e)); - - let ts_lang = get_language_for_key(&lang_key); - - let const_name = syn::Ident::new( - &format!("{}_NODE_TYPES", lang_key.to_uppercase()), - Span::call_site(), - ); - - let generated = generate_static_node_types_code(&raw_nodes, &ts_lang, &lang_key, &const_name); - - generated.into() -} - -fn get_language_for_key(key: &str) -> Language { - match key.to_lowercase().as_str() { - #[cfg(feature = "lang-ada")] - "ada" => arborium_ada::language().into(), - #[cfg(feature = "lang-agda")] - "agda" => arborium_agda::language().into(), - #[cfg(feature = "lang-asciidoc")] - "asciidoc" => arborium_asciidoc::language().into(), - #[cfg(feature = "lang-asm")] - "asm" => arborium_asm::language().into(), - #[cfg(feature = "lang-awk")] - "awk" => arborium_awk::language().into(), - #[cfg(feature = "lang-bash")] - "bash" => arborium_bash::language().into(), - #[cfg(feature = "lang-batch")] - "batch" => arborium_batch::language().into(), - #[cfg(feature = "lang-c")] - "c" => arborium_c::language().into(), - #[cfg(feature = "lang-c-sharp")] - "c_sharp" => arborium_c_sharp::language().into(), - #[cfg(feature = "lang-caddy")] - "caddy" => arborium_caddy::language().into(), - #[cfg(feature = "lang-capnp")] - "capnp" => arborium_capnp::language().into(), - #[cfg(feature = "lang-clojure")] - "clojure" => arborium_clojure::language().into(), - #[cfg(feature = "lang-cmake")] - "cmake" => arborium_cmake::language().into(), - #[cfg(feature = "lang-commonlisp")] - "commonlisp" => arborium_commonlisp::language().into(), - #[cfg(feature = "lang-cpp")] - "cpp" => arborium_cpp::language().into(), - #[cfg(feature = "lang-css")] - "css" => arborium_css::language().into(), - #[cfg(feature = "lang-d")] - "d" => arborium_d::language().into(), - #[cfg(feature = "lang-dart")] - "dart" => arborium_dart::language().into(), - #[cfg(feature = "lang-devicetree")] - "devicetree" => arborium_devicetree::language().into(), - #[cfg(feature = "lang-diff")] - "diff" => arborium_diff::language().into(), - #[cfg(feature = "lang-dockerfile")] - "dockerfile" => arborium_dockerfile::language().into(), - #[cfg(feature = "lang-dot")] - "dot" => arborium_dot::language().into(), - #[cfg(feature = "lang-elisp")] - "elisp" => arborium_elisp::language().into(), - #[cfg(feature = "lang-elixir")] - "elixir" => arborium_elixir::language().into(), - #[cfg(feature = "lang-elm")] - "elm" => arborium_elm::language().into(), - #[cfg(feature = "lang-erlang")] - "erlang" => arborium_erlang::language().into(), - #[cfg(feature = "lang-fish")] - "fish" => arborium_fish::language().into(), - #[cfg(feature = "lang-fsharp")] - "fsharp" => arborium_fsharp::language().into(), - #[cfg(feature = "lang-gleam")] - "gleam" => arborium_gleam::language().into(), - #[cfg(feature = "lang-glsl")] - "glsl" => arborium_glsl::language().into(), - #[cfg(feature = "lang-go")] - "go" => arborium_go::language().into(), - #[cfg(feature = "lang-graphql")] - "graphql" => arborium_graphql::language().into(), - #[cfg(feature = "lang-groovy")] - "groovy" => arborium_groovy::language().into(), - #[cfg(feature = "lang-haskell")] - "haskell" => arborium_haskell::language().into(), - #[cfg(feature = "lang-hcl")] - "hcl" => arborium_hcl::language().into(), - #[cfg(feature = "lang-hlsl")] - "hlsl" => arborium_hlsl::language().into(), - #[cfg(feature = "lang-html")] - "html" => arborium_html::language().into(), - #[cfg(feature = "lang-idris")] - "idris" => arborium_idris::language().into(), - #[cfg(feature = "lang-ini")] - "ini" => arborium_ini::language().into(), - #[cfg(feature = "lang-java")] - "java" => arborium_java::language().into(), - #[cfg(feature = "lang-javascript")] - "javascript" => arborium_javascript::language().into(), - #[cfg(feature = "lang-jinja2")] - "jinja2" => arborium_jinja2::language().into(), - #[cfg(feature = "lang-jq")] - "jq" => arborium_jq::language().into(), - #[cfg(feature = "lang-json")] - "json" => arborium_json::language().into(), - #[cfg(feature = "lang-julia")] - "julia" => arborium_julia::language().into(), - #[cfg(feature = "lang-kdl")] - "kdl" => arborium_kdl::language().into(), - #[cfg(feature = "lang-kotlin")] - "kotlin" => arborium_kotlin::language().into(), - #[cfg(feature = "lang-lean")] - "lean" => arborium_lean::language().into(), - #[cfg(feature = "lang-lua")] - "lua" => arborium_lua::language().into(), - #[cfg(feature = "lang-markdown")] - "markdown" => arborium_markdown::language().into(), - #[cfg(feature = "lang-matlab")] - "matlab" => arborium_matlab::language().into(), - #[cfg(feature = "lang-meson")] - "meson" => arborium_meson::language().into(), - #[cfg(feature = "lang-nginx")] - "nginx" => arborium_nginx::language().into(), - #[cfg(feature = "lang-ninja")] - "ninja" => arborium_ninja::language().into(), - #[cfg(feature = "lang-nix")] - "nix" => arborium_nix::language().into(), - #[cfg(feature = "lang-objc")] - "objc" => arborium_objc::language().into(), - #[cfg(feature = "lang-ocaml")] - "ocaml" => arborium_ocaml::language().into(), - #[cfg(feature = "lang-perl")] - "perl" => arborium_perl::language().into(), - #[cfg(feature = "lang-php")] - "php" => arborium_php::language().into(), - #[cfg(feature = "lang-postscript")] - "postscript" => arborium_postscript::language().into(), - #[cfg(feature = "lang-powershell")] - "powershell" => arborium_powershell::language().into(), - #[cfg(feature = "lang-prolog")] - "prolog" => arborium_prolog::language().into(), - #[cfg(feature = "lang-python")] - "python" => arborium_python::language().into(), - #[cfg(feature = "lang-query")] - "query" => arborium_query::language().into(), - #[cfg(feature = "lang-r")] - "r" => arborium_r::language().into(), - #[cfg(feature = "lang-rescript")] - "rescript" => arborium_rescript::language().into(), - #[cfg(feature = "lang-ron")] - "ron" => arborium_ron::language().into(), - #[cfg(feature = "lang-ruby")] - "ruby" => arborium_ruby::language().into(), - #[cfg(feature = "lang-rust")] - "rust" => arborium_rust::language().into(), - #[cfg(feature = "lang-scala")] - "scala" => arborium_scala::language().into(), - #[cfg(feature = "lang-scheme")] - "scheme" => arborium_scheme::language().into(), - #[cfg(feature = "lang-scss")] - "scss" => arborium_scss::language().into(), - #[cfg(feature = "lang-sparql")] - "sparql" => arborium_sparql::language().into(), - #[cfg(feature = "lang-sql")] - "sql" => arborium_sql::language().into(), - #[cfg(feature = "lang-ssh-config")] - "ssh_config" => arborium_ssh_config::language().into(), - #[cfg(feature = "lang-starlark")] - "starlark" => arborium_starlark::language().into(), - #[cfg(feature = "lang-svelte")] - "svelte" => arborium_svelte::language().into(), - #[cfg(feature = "lang-swift")] - "swift" => arborium_swift::language().into(), - #[cfg(feature = "lang-textproto")] - "textproto" => arborium_textproto::language().into(), - #[cfg(feature = "lang-thrift")] - "thrift" => arborium_thrift::language().into(), - #[cfg(feature = "lang-tlaplus")] - "tlaplus" => arborium_tlaplus::language().into(), - #[cfg(feature = "lang-toml")] - "toml" => arborium_toml::language().into(), - #[cfg(feature = "lang-tsx")] - "tsx" => arborium_tsx::language().into(), - #[cfg(feature = "lang-typescript")] - "typescript" => arborium_typescript::language().into(), - #[cfg(feature = "lang-typst")] - "typst" => arborium_typst::language().into(), - #[cfg(feature = "lang-uiua")] - "uiua" => arborium_uiua::language().into(), - #[cfg(feature = "lang-vb")] - "vb" => arborium_vb::language().into(), - #[cfg(feature = "lang-verilog")] - "verilog" => arborium_verilog::language().into(), - #[cfg(feature = "lang-vhdl")] - "vhdl" => arborium_vhdl::language().into(), - #[cfg(feature = "lang-vim")] - "vim" => arborium_vim::language().into(), - #[cfg(feature = "lang-vue")] - "vue" => arborium_vue::language().into(), - #[cfg(feature = "lang-wit")] - "wit" => arborium_wit::language().into(), - #[cfg(feature = "lang-x86asm")] - "x86asm" => arborium_x86asm::language().into(), - #[cfg(feature = "lang-xml")] - "xml" => arborium_xml::language().into(), - #[cfg(feature = "lang-yaml")] - "yaml" => arborium_yaml::language().into(), - #[cfg(feature = "lang-yuri")] - "yuri" => arborium_yuri::language().into(), - #[cfg(feature = "lang-zig")] - "zig" => arborium_zig::language().into(), - #[cfg(feature = "lang-zsh")] - "zsh" => arborium_zsh::language().into(), - _ => panic!("Unknown or disabled language key: {}", key), - } -} - -struct FieldCodeGen { - array_defs: Vec, - entries: Vec, -} - -fn generate_field_code( - prefix: &str, - node_id: std::num::NonZeroU16, - field_id: &std::num::NonZeroU16, - field_info: &plotnik_core::FieldInfo, -) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { - let valid_types_raw: Vec = field_info.valid_types.iter().map(|id| id.get()).collect(); - let valid_types_name = syn::Ident::new( - &format!("{}_N{}_F{}_TYPES", prefix, node_id.get(), field_id), - Span::call_site(), - ); - - let multiple = field_info.cardinality.multiple; - let required = field_info.cardinality.required; - let types_len = valid_types_raw.len(); - - let array_def = quote! { - static #valid_types_name: [std::num::NonZeroU16; #types_len] = [ - #(std::num::NonZeroU16::new(#valid_types_raw).unwrap()),* - ]; - }; - - let field_id_raw = field_id.get(); - let entry = quote! { - (std::num::NonZeroU16::new(#field_id_raw).unwrap(), plotnik_core::StaticFieldInfo { - cardinality: plotnik_core::Cardinality { - multiple: #multiple, - required: #required, - }, - valid_types: &#valid_types_name, - }) - }; - - (array_def, entry) -} - -fn generate_fields_for_node( - prefix: &str, - node_id: std::num::NonZeroU16, - fields: &std::collections::HashMap, -) -> FieldCodeGen { - let mut sorted_fields: Vec<_> = fields.iter().collect(); - sorted_fields.sort_by_key(|(fid, _)| *fid); - - let mut array_defs = Vec::new(); - let mut entries = Vec::new(); - - for (field_id, field_info) in sorted_fields { - let (array_def, entry) = generate_field_code(prefix, node_id, field_id, field_info); - array_defs.push(array_def); - entries.push(entry); - } - - FieldCodeGen { - array_defs, - entries, - } -} - -fn generate_children_code( - prefix: &str, - node_id: std::num::NonZeroU16, - children: &plotnik_core::ChildrenInfo, - static_defs: &mut Vec, -) -> proc_macro2::TokenStream { - let valid_types_raw: Vec = children.valid_types.iter().map(|id| id.get()).collect(); - let children_types_name = syn::Ident::new( - &format!("{}_N{}_CHILDREN_TYPES", prefix, node_id.get()), - Span::call_site(), - ); - let types_len = valid_types_raw.len(); - - static_defs.push(quote! { - static #children_types_name: [std::num::NonZeroU16; #types_len] = [ - #(std::num::NonZeroU16::new(#valid_types_raw).unwrap()),* - ]; - }); - - let multiple = children.cardinality.multiple; - let required = children.cardinality.required; - - quote! { - Some(plotnik_core::StaticChildrenInfo { - cardinality: plotnik_core::Cardinality { - multiple: #multiple, - required: #required, - }, - valid_types: &#children_types_name, - }) - } -} - -fn generate_static_node_types_code( - raw_nodes: &[plotnik_core::RawNode], - ts_lang: &Language, - lang_key: &str, - const_name: &syn::Ident, -) -> proc_macro2::TokenStream { - let node_types = plotnik_core::DynamicNodeTypes::build( - raw_nodes, - |name, named| { - let id = ts_lang.id_for_node_kind(name, named); - std::num::NonZeroU16::new(id) - }, - |name| ts_lang.field_id_for_name(name), - ); - - let prefix = lang_key.to_uppercase(); - let mut static_defs = Vec::new(); - let mut node_entries = Vec::new(); - - let extras_raw: Vec = node_types - .sorted_extras() - .iter() - .map(|id| id.get()) - .collect(); - let root = node_types.root(); - let sorted_node_ids = node_types.sorted_node_ids(); - - for &node_id in &sorted_node_ids { - let info = node_types.get(node_id).unwrap(); - - let node_id_raw = node_id.get(); - let field_gen = generate_fields_for_node(&prefix, node_id, &info.fields); - static_defs.extend(field_gen.array_defs); - - let fields_ref = if field_gen.entries.is_empty() { - quote! { &[] } - } else { - let fields_array_name = syn::Ident::new( - &format!("{}_N{}_FIELDS", prefix, node_id_raw), - Span::call_site(), - ); - let fields_len = field_gen.entries.len(); - let field_entries = &field_gen.entries; - - static_defs.push(quote! { - static #fields_array_name: [(std::num::NonZeroU16, plotnik_core::StaticFieldInfo); #fields_len] = [ - #(#field_entries),* - ]; - }); - - quote! { &#fields_array_name } - }; - - let children_code = match &info.children { - Some(children) => generate_children_code(&prefix, node_id, children, &mut static_defs), - None => quote! { None }, - }; - - let name = &info.name; - let named = info.named; - - node_entries.push(quote! { - (std::num::NonZeroU16::new(#node_id_raw).unwrap(), plotnik_core::StaticNodeTypeInfo { - name: #name, - named: #named, - fields: #fields_ref, - children: #children_code, - }) - }); - } - - let nodes_array_name = syn::Ident::new(&format!("{}_NODES", prefix), Span::call_site()); - let nodes_len = sorted_node_ids.len(); - - let extras_array_name = syn::Ident::new(&format!("{}_EXTRAS", prefix), Span::call_site()); - let extras_len = extras_raw.len(); - - let root_code = match root { - Some(id) => { - let id_raw = id.get(); - quote! { Some(std::num::NonZeroU16::new(#id_raw).unwrap()) } - } - None => quote! { None }, - }; - - quote! { - #(#static_defs)* - - static #nodes_array_name: [(std::num::NonZeroU16, plotnik_core::StaticNodeTypeInfo); #nodes_len] = [ - #(#node_entries),* - ]; - - static #extras_array_name: [std::num::NonZeroU16; #extras_len] = [ - #(std::num::NonZeroU16::new(#extras_raw).unwrap()),* - ]; - - pub static #const_name: plotnik_core::StaticNodeTypes = plotnik_core::StaticNodeTypes::new( - &#nodes_array_name, - &#extras_array_name, - #root_code, - ); - } -}