cst: eliminate manual synchronization between enum variants and numeric values

This commit is contained in:
raf 2025-06-02 14:32:36 +03:00
commit b9d8cb6d5d
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
3 changed files with 260 additions and 221 deletions

57
Cargo.lock generated
View file

@ -281,6 +281,7 @@ dependencies = [
"cstree",
"glob",
"logos",
"num_enum",
"regex",
"serde",
"serde_json",
@ -288,6 +289,27 @@ dependencies = [
"thiserror",
]
[[package]]
name = "num_enum"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "parking_lot"
version = "0.12.3"
@ -311,6 +333,15 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "proc-macro-crate"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
@ -487,6 +518,23 @@ dependencies = [
"syn",
]
[[package]]
name = "toml_datetime"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
[[package]]
name = "toml_edit"
version = "0.22.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "triomphe"
version = "0.1.14"
@ -573,3 +621,12 @@ name = "windows_x86_64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "winnow"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
dependencies = [
"memchr",
]

View file

@ -14,5 +14,6 @@ cstree = "0.12"
text-size = "1.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
regex = "1.11.1"
regex = "1.11"
glob = "0.3"
num_enum = "0.7"

View file

@ -4,11 +4,15 @@
use crate::lexer::{Token, TokenKind};
use cstree::{RawSyntaxKind, green::GreenNode, util::NodeOrToken};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::fmt;
use thiserror::Error;
/// nftables syntax node types
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
/// Uses `TryFromPrimitive` for safe conversion from raw values with fallback to `Error`.
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TryFromPrimitive, IntoPrimitive,
)]
#[repr(u16)]
pub enum SyntaxKind {
// Root and containers
@ -161,116 +165,128 @@ pub enum SyntaxKind {
impl From<TokenKind> for SyntaxKind {
fn from(kind: TokenKind) -> Self {
use TokenKind::*;
match kind {
TokenKind::Table => SyntaxKind::TableKw,
TokenKind::Chain => SyntaxKind::ChainKw,
TokenKind::Rule => SyntaxKind::RuleKw,
TokenKind::Set => SyntaxKind::SetKw,
TokenKind::Map => SyntaxKind::MapKw,
TokenKind::Element => SyntaxKind::ElementKw,
TokenKind::Include => SyntaxKind::IncludeKw,
TokenKind::Define => SyntaxKind::DefineKw,
TokenKind::Flush => SyntaxKind::FlushKw,
TokenKind::Add => SyntaxKind::AddKw,
TokenKind::Delete => SyntaxKind::DeleteKw,
TokenKind::Insert => SyntaxKind::InsertKw,
TokenKind::Replace => SyntaxKind::ReplaceKw,
// Keywords -> Kw variants
Table => SyntaxKind::TableKw,
Chain => SyntaxKind::ChainKw,
Rule => SyntaxKind::RuleKw,
Set => SyntaxKind::SetKw,
Map => SyntaxKind::MapKw,
Element => SyntaxKind::ElementKw,
Include => SyntaxKind::IncludeKw,
Define => SyntaxKind::DefineKw,
Flush => SyntaxKind::FlushKw,
Add => SyntaxKind::AddKw,
Delete => SyntaxKind::DeleteKw,
Insert => SyntaxKind::InsertKw,
Replace => SyntaxKind::ReplaceKw,
TokenKind::Filter => SyntaxKind::FilterKw,
TokenKind::Nat => SyntaxKind::NatKw,
TokenKind::Route => SyntaxKind::RouteKw,
// Chain types and hooks
Filter => SyntaxKind::FilterKw,
Nat => SyntaxKind::NatKw,
Route => SyntaxKind::RouteKw,
Input => SyntaxKind::InputKw,
Output => SyntaxKind::OutputKw,
Forward => SyntaxKind::ForwardKw,
Prerouting => SyntaxKind::PreroutingKw,
Postrouting => SyntaxKind::PostroutingKw,
TokenKind::Input => SyntaxKind::InputKw,
TokenKind::Output => SyntaxKind::OutputKw,
TokenKind::Forward => SyntaxKind::ForwardKw,
TokenKind::Prerouting => SyntaxKind::PreroutingKw,
TokenKind::Postrouting => SyntaxKind::PostroutingKw,
// Protocols and families
Ip => SyntaxKind::IpKw,
Ip6 => SyntaxKind::Ip6Kw,
Inet => SyntaxKind::InetKw,
Arp => SyntaxKind::ArpKw,
Bridge => SyntaxKind::BridgeKw,
Netdev => SyntaxKind::NetdevKw,
Tcp => SyntaxKind::TcpKw,
Udp => SyntaxKind::UdpKw,
Icmp => SyntaxKind::IcmpKw,
Icmpv6 => SyntaxKind::Icmpv6Kw,
TokenKind::Ip => SyntaxKind::IpKw,
TokenKind::Ip6 => SyntaxKind::Ip6Kw,
TokenKind::Inet => SyntaxKind::InetKw,
TokenKind::Arp => SyntaxKind::ArpKw,
TokenKind::Bridge => SyntaxKind::BridgeKw,
TokenKind::Netdev => SyntaxKind::NetdevKw,
TokenKind::Tcp => SyntaxKind::TcpKw,
TokenKind::Udp => SyntaxKind::UdpKw,
TokenKind::Icmp => SyntaxKind::IcmpKw,
TokenKind::Icmpv6 => SyntaxKind::Icmpv6Kw,
// Match keywords
Sport => SyntaxKind::SportKw,
Dport => SyntaxKind::DportKw,
Saddr => SyntaxKind::SaddrKw,
Daddr => SyntaxKind::DaddrKw,
Protocol => SyntaxKind::ProtocolKw,
Nexthdr => SyntaxKind::NexthdrKw,
Type => SyntaxKind::TypeKw,
Hook => SyntaxKind::HookKw,
Priority => SyntaxKind::PriorityKw,
Policy => SyntaxKind::PolicyKw,
Iifname => SyntaxKind::IifnameKw,
Oifname => SyntaxKind::OifnameKw,
Ct => SyntaxKind::CtKw,
State => SyntaxKind::StateKw,
TokenKind::Sport => SyntaxKind::SportKw,
TokenKind::Dport => SyntaxKind::DportKw,
TokenKind::Saddr => SyntaxKind::SaddrKw,
TokenKind::Daddr => SyntaxKind::DaddrKw,
TokenKind::Protocol => SyntaxKind::ProtocolKw,
TokenKind::Nexthdr => SyntaxKind::NexthdrKw,
TokenKind::Type => SyntaxKind::TypeKw,
TokenKind::Hook => SyntaxKind::HookKw,
TokenKind::Priority => SyntaxKind::PriorityKw,
TokenKind::Policy => SyntaxKind::PolicyKw,
TokenKind::Iifname => SyntaxKind::IifnameKw,
TokenKind::Oifname => SyntaxKind::OifnameKw,
TokenKind::Ct => SyntaxKind::CtKw,
TokenKind::State => SyntaxKind::StateKw,
// Actions
Accept => SyntaxKind::AcceptKw,
Drop => SyntaxKind::DropKw,
Reject => SyntaxKind::RejectKw,
Return => SyntaxKind::ReturnKw,
Jump => SyntaxKind::JumpKw,
Goto => SyntaxKind::GotoKw,
Continue => SyntaxKind::ContinueKw,
Log => SyntaxKind::LogKw,
Comment => SyntaxKind::CommentKw,
TokenKind::Accept => SyntaxKind::AcceptKw,
TokenKind::Drop => SyntaxKind::DropKw,
TokenKind::Reject => SyntaxKind::RejectKw,
TokenKind::Return => SyntaxKind::ReturnKw,
TokenKind::Jump => SyntaxKind::JumpKw,
TokenKind::Goto => SyntaxKind::GotoKw,
TokenKind::Continue => SyntaxKind::ContinueKw,
TokenKind::Log => SyntaxKind::LogKw,
TokenKind::Comment => SyntaxKind::CommentKw,
// States
Established => SyntaxKind::EstablishedKw,
Related => SyntaxKind::RelatedKw,
New => SyntaxKind::NewKw,
Invalid => SyntaxKind::InvalidKw,
TokenKind::Established => SyntaxKind::EstablishedKw,
TokenKind::Related => SyntaxKind::RelatedKw,
TokenKind::New => SyntaxKind::NewKw,
TokenKind::Invalid => SyntaxKind::InvalidKw,
// Additional protocol keywords
Vmap => SyntaxKind::VmapKw,
NdRouterAdvert => SyntaxKind::NdRouterAdvertKw,
NdNeighborSolicit => SyntaxKind::NdNeighborSolicitKw,
NdNeighborAdvert => SyntaxKind::NdNeighborAdvertKw,
EchoRequest => SyntaxKind::EchoRequestKw,
DestUnreachable => SyntaxKind::DestUnreachableKw,
RouterAdvertisement => SyntaxKind::RouterAdvertisementKw,
TimeExceeded => SyntaxKind::TimeExceededKw,
ParameterProblem => SyntaxKind::ParameterProblemKw,
PacketTooBig => SyntaxKind::PacketTooBigKw,
TokenKind::Vmap => SyntaxKind::VmapKw,
TokenKind::NdRouterAdvert => SyntaxKind::NdRouterAdvertKw,
TokenKind::NdNeighborSolicit => SyntaxKind::NdNeighborSolicitKw,
TokenKind::NdNeighborAdvert => SyntaxKind::NdNeighborAdvertKw,
TokenKind::EchoRequest => SyntaxKind::EchoRequestKw,
TokenKind::DestUnreachable => SyntaxKind::DestUnreachableKw,
TokenKind::RouterAdvertisement => SyntaxKind::RouterAdvertisementKw,
TokenKind::TimeExceeded => SyntaxKind::TimeExceededKw,
TokenKind::ParameterProblem => SyntaxKind::ParameterProblemKw,
TokenKind::PacketTooBig => SyntaxKind::PacketTooBigKw,
// Operators - direct mapping
Eq => SyntaxKind::EqOp,
Ne => SyntaxKind::NeOp,
Le => SyntaxKind::LeOp,
Ge => SyntaxKind::GeOp,
Lt => SyntaxKind::LtOp,
Gt => SyntaxKind::GtOp,
TokenKind::Eq => SyntaxKind::EqOp,
TokenKind::Ne => SyntaxKind::NeOp,
TokenKind::Le => SyntaxKind::LeOp,
TokenKind::Ge => SyntaxKind::GeOp,
TokenKind::Lt => SyntaxKind::LtOp,
TokenKind::Gt => SyntaxKind::GtOp,
// Punctuation - direct mapping
LeftBrace => SyntaxKind::LeftBrace,
RightBrace => SyntaxKind::RightBrace,
LeftParen => SyntaxKind::LeftParen,
RightParen => SyntaxKind::RightParen,
LeftBracket => SyntaxKind::LeftBracket,
RightBracket => SyntaxKind::RightBracket,
Comma => SyntaxKind::Comma,
Semicolon => SyntaxKind::Semicolon,
Colon => SyntaxKind::Colon,
Assign => SyntaxKind::Assign,
Dash => SyntaxKind::Dash,
Slash => SyntaxKind::Slash,
Dot => SyntaxKind::Dot,
TokenKind::LeftBrace => SyntaxKind::LeftBrace,
TokenKind::RightBrace => SyntaxKind::RightBrace,
TokenKind::LeftParen => SyntaxKind::LeftParen,
TokenKind::RightParen => SyntaxKind::RightParen,
TokenKind::LeftBracket => SyntaxKind::LeftBracket,
TokenKind::RightBracket => SyntaxKind::RightBracket,
TokenKind::Comma => SyntaxKind::Comma,
TokenKind::Semicolon => SyntaxKind::Semicolon,
TokenKind::Colon => SyntaxKind::Colon,
TokenKind::Assign => SyntaxKind::Assign,
TokenKind::Dash => SyntaxKind::Dash,
TokenKind::Slash => SyntaxKind::Slash,
TokenKind::Dot => SyntaxKind::Dot,
// Literals - map data-carrying variants to their types
StringLiteral(_) => SyntaxKind::StringLiteral,
NumberLiteral(_) => SyntaxKind::NumberLiteral,
IpAddress(_) => SyntaxKind::IpAddress,
Ipv6Address(_) => SyntaxKind::Ipv6Address,
MacAddress(_) => SyntaxKind::MacAddress,
Identifier(_) => SyntaxKind::Identifier,
TokenKind::StringLiteral(_) => SyntaxKind::StringLiteral,
TokenKind::NumberLiteral(_) => SyntaxKind::NumberLiteral,
TokenKind::IpAddress(_) => SyntaxKind::IpAddress,
TokenKind::Ipv6Address(_) => SyntaxKind::Ipv6Address,
TokenKind::MacAddress(_) => SyntaxKind::MacAddress,
TokenKind::Identifier(_) => SyntaxKind::Identifier,
// Special tokens
Newline => SyntaxKind::Newline,
CommentLine(_) => SyntaxKind::Comment,
Shebang(_) => SyntaxKind::Shebang,
TokenKind::Newline => SyntaxKind::Newline,
TokenKind::CommentLine(_) => SyntaxKind::Comment,
TokenKind::Shebang(_) => SyntaxKind::Shebang,
TokenKind::Error => SyntaxKind::Error,
// Error fallback
Error => SyntaxKind::Error,
}
}
}
@ -324,126 +340,7 @@ impl SyntaxKind {
}
pub fn from_raw(raw: RawSyntaxKind) -> Self {
match raw.0 {
0 => SyntaxKind::Root,
1 => SyntaxKind::Table,
2 => SyntaxKind::Chain,
3 => SyntaxKind::Rule,
4 => SyntaxKind::Set,
5 => SyntaxKind::Map,
6 => SyntaxKind::Element,
7 => SyntaxKind::Expression,
8 => SyntaxKind::BinaryExpr,
9 => SyntaxKind::UnaryExpr,
10 => SyntaxKind::CallExpr,
11 => SyntaxKind::SetExpr,
12 => SyntaxKind::RangeExpr,
13 => SyntaxKind::Statement,
14 => SyntaxKind::IncludeStmt,
15 => SyntaxKind::DefineStmt,
16 => SyntaxKind::FlushStmt,
17 => SyntaxKind::AddStmt,
18 => SyntaxKind::DeleteStmt,
19 => SyntaxKind::Identifier,
20 => SyntaxKind::StringLiteral,
21 => SyntaxKind::NumberLiteral,
22 => SyntaxKind::IpAddress,
23 => SyntaxKind::Ipv6Address,
24 => SyntaxKind::MacAddress,
25 => SyntaxKind::TableKw,
26 => SyntaxKind::ChainKw,
27 => SyntaxKind::RuleKw,
28 => SyntaxKind::SetKw,
29 => SyntaxKind::MapKw,
30 => SyntaxKind::ElementKw,
31 => SyntaxKind::IncludeKw,
32 => SyntaxKind::DefineKw,
33 => SyntaxKind::FlushKw,
34 => SyntaxKind::AddKw,
35 => SyntaxKind::DeleteKw,
36 => SyntaxKind::InsertKw,
37 => SyntaxKind::ReplaceKw,
38 => SyntaxKind::FilterKw,
39 => SyntaxKind::NatKw,
40 => SyntaxKind::RouteKw,
41 => SyntaxKind::InputKw,
42 => SyntaxKind::OutputKw,
43 => SyntaxKind::ForwardKw,
44 => SyntaxKind::PreroutingKw,
45 => SyntaxKind::PostroutingKw,
46 => SyntaxKind::IpKw,
47 => SyntaxKind::Ip6Kw,
48 => SyntaxKind::InetKw,
49 => SyntaxKind::ArpKw,
50 => SyntaxKind::BridgeKw,
51 => SyntaxKind::NetdevKw,
52 => SyntaxKind::TcpKw,
53 => SyntaxKind::UdpKw,
54 => SyntaxKind::IcmpKw,
55 => SyntaxKind::Icmpv6Kw,
56 => SyntaxKind::SportKw,
57 => SyntaxKind::DportKw,
58 => SyntaxKind::SaddrKw,
59 => SyntaxKind::DaddrKw,
60 => SyntaxKind::ProtocolKw,
61 => SyntaxKind::NexthdrKw,
62 => SyntaxKind::TypeKw,
63 => SyntaxKind::HookKw,
64 => SyntaxKind::PriorityKw,
65 => SyntaxKind::PolicyKw,
66 => SyntaxKind::IifnameKw,
67 => SyntaxKind::OifnameKw,
68 => SyntaxKind::CtKw,
69 => SyntaxKind::StateKw,
70 => SyntaxKind::AcceptKw,
71 => SyntaxKind::DropKw,
72 => SyntaxKind::RejectKw,
73 => SyntaxKind::ReturnKw,
74 => SyntaxKind::JumpKw,
75 => SyntaxKind::GotoKw,
76 => SyntaxKind::ContinueKw,
77 => SyntaxKind::LogKw,
78 => SyntaxKind::CommentKw,
79 => SyntaxKind::EstablishedKw,
80 => SyntaxKind::RelatedKw,
81 => SyntaxKind::NewKw,
82 => SyntaxKind::InvalidKw,
83 => SyntaxKind::EqOp,
84 => SyntaxKind::NeOp,
85 => SyntaxKind::LeOp,
86 => SyntaxKind::GeOp,
87 => SyntaxKind::LtOp,
88 => SyntaxKind::GtOp,
89 => SyntaxKind::LeftBrace,
90 => SyntaxKind::RightBrace,
91 => SyntaxKind::LeftParen,
92 => SyntaxKind::RightParen,
93 => SyntaxKind::LeftBracket,
94 => SyntaxKind::RightBracket,
95 => SyntaxKind::Comma,
96 => SyntaxKind::Semicolon,
97 => SyntaxKind::Colon,
98 => SyntaxKind::Assign,
99 => SyntaxKind::Dash,
100 => SyntaxKind::Slash,
101 => SyntaxKind::Dot,
102 => SyntaxKind::Whitespace,
103 => SyntaxKind::Newline,
104 => SyntaxKind::Comment,
105 => SyntaxKind::Shebang,
106 => SyntaxKind::Error,
107 => SyntaxKind::VmapKw,
108 => SyntaxKind::NdRouterAdvertKw,
109 => SyntaxKind::NdNeighborSolicitKw,
110 => SyntaxKind::NdNeighborAdvertKw,
111 => SyntaxKind::EchoRequestKw,
112 => SyntaxKind::DestUnreachableKw,
113 => SyntaxKind::RouterAdvertisementKw,
114 => SyntaxKind::TimeExceededKw,
115 => SyntaxKind::ParameterProblemKw,
116 => SyntaxKind::PacketTooBigKw,
_ => SyntaxKind::Error, // Fallback to Error for invalid values
}
Self::try_from(raw.0 as u16).unwrap_or(SyntaxKind::Error)
}
}
@ -1350,7 +1247,7 @@ mod tests {
let mut lexer = NftablesLexer::new(source);
let tokens = lexer.tokenize().expect("Tokenization should succeed");
// CST is now implemented - test that it works
// Test CST construction with basic table syntax
let green_tree = CstBuilder::build_tree(&tokens);
// Verify the tree was created successfully
@ -1367,4 +1264,88 @@ mod tests {
let cst_result = CstBuilder::parse_to_cst(&tokens);
assert!(cst_result.is_ok());
}
#[test]
fn test_num_enum_improvements() {
// Test that from_raw uses num_enum for conversion
// Invalid values fall back to Error variant
// Test valid conversions
assert_eq!(SyntaxKind::from_raw(RawSyntaxKind(0)), SyntaxKind::Root);
assert_eq!(SyntaxKind::from_raw(RawSyntaxKind(1)), SyntaxKind::Table);
assert_eq!(SyntaxKind::from_raw(RawSyntaxKind(25)), SyntaxKind::TableKw);
assert_eq!(SyntaxKind::from_raw(RawSyntaxKind(106)), SyntaxKind::Error);
assert_eq!(
SyntaxKind::from_raw(RawSyntaxKind(116)),
SyntaxKind::PacketTooBigKw
);
// Test invalid values automatically fall back to Error
assert_eq!(SyntaxKind::from_raw(RawSyntaxKind(999)), SyntaxKind::Error);
assert_eq!(SyntaxKind::from_raw(RawSyntaxKind(1000)), SyntaxKind::Error);
// Test bidirectional conversion
for variant in [
SyntaxKind::Root,
SyntaxKind::Table,
SyntaxKind::TableKw,
SyntaxKind::Error,
SyntaxKind::PacketTooBigKw,
] {
let raw = variant.to_raw();
let converted_back = SyntaxKind::from_raw(raw);
assert_eq!(variant, converted_back);
}
}
#[test]
fn test_token_kind_conversion_improvements() {
// Test that From<TokenKind> conversion is complete and correct
use crate::lexer::TokenKind;
// Test keyword mappings
assert_eq!(SyntaxKind::from(TokenKind::Table), SyntaxKind::TableKw);
assert_eq!(SyntaxKind::from(TokenKind::Chain), SyntaxKind::ChainKw);
assert_eq!(SyntaxKind::from(TokenKind::Accept), SyntaxKind::AcceptKw);
// Test operators
assert_eq!(SyntaxKind::from(TokenKind::Eq), SyntaxKind::EqOp);
assert_eq!(SyntaxKind::from(TokenKind::Lt), SyntaxKind::LtOp);
// Test punctuation
assert_eq!(
SyntaxKind::from(TokenKind::LeftBrace),
SyntaxKind::LeftBrace
);
assert_eq!(
SyntaxKind::from(TokenKind::Semicolon),
SyntaxKind::Semicolon
);
// Test literals (with data)
assert_eq!(
SyntaxKind::from(TokenKind::StringLiteral("test".to_string())),
SyntaxKind::StringLiteral
);
assert_eq!(
SyntaxKind::from(TokenKind::NumberLiteral(42)),
SyntaxKind::NumberLiteral
);
assert_eq!(
SyntaxKind::from(TokenKind::IpAddress("192.168.1.1".to_string())),
SyntaxKind::IpAddress
);
assert_eq!(
SyntaxKind::from(TokenKind::Identifier("test".to_string())),
SyntaxKind::Identifier
);
// Test special tokens
assert_eq!(SyntaxKind::from(TokenKind::Newline), SyntaxKind::Newline);
assert_eq!(
SyntaxKind::from(TokenKind::CommentLine("# comment".to_string())),
SyntaxKind::Comment
);
assert_eq!(SyntaxKind::from(TokenKind::Error), SyntaxKind::Error);
}
}