nff/src/parser.rs
2025-05-24 23:41:06 +03:00

1087 lines
40 KiB
Rust

use crate::ast::*;
use crate::lexer::{LexError, Token, TokenKind};
use anyhow::{Result, anyhow};
use thiserror::Error;
/// Parse errors for nftables configuration
#[derive(Error, Debug)]
pub enum ParseError {
#[error(
"Unexpected token at line {line}, column {column}: expected {expected}, found '{found}'"
)]
UnexpectedToken {
line: usize,
column: usize,
expected: String,
found: String,
},
#[error("Missing token: expected {expected}")]
MissingToken { expected: String },
#[error("Invalid expression: {message}")]
InvalidExpression { message: String },
#[error("Invalid statement: {message}")]
InvalidStatement { message: String },
#[error("Lexical error: {0}")]
LexError(#[from] LexError),
#[error("Semantic error: {message}")]
SemanticError { message: String },
#[error("Anyhow error: {0}")]
AnyhowError(#[from] anyhow::Error),
}
/// Result type for parsing operations
pub type ParseResult<T> = Result<T, ParseError>;
/// Parsing context for maintaining state during parsing
#[derive(Debug)]
pub struct ParseContext {
pub in_table: bool,
pub in_chain: bool,
pub in_rule: bool,
pub current_table: Option<String>,
pub current_chain: Option<String>,
}
impl ParseContext {
fn new() -> Self {
Self {
in_table: false,
in_chain: false,
in_rule: false,
current_table: None,
current_chain: None,
}
}
}
/// Parser for nftables configuration files
#[derive(Debug)]
pub struct Parser {
tokens: Vec<Token>,
current: usize,
context: ParseContext,
}
impl Parser {
/// Create a new parser with the given tokens
pub fn new(tokens: Vec<Token>) -> Self {
Self {
tokens,
current: 0,
context: ParseContext::new(),
}
}
/// Parse the entire ruleset
pub fn parse(&mut self) -> ParseResult<Ruleset> {
let mut ruleset = Ruleset::new();
// Skip initial whitespace and newlines
self.skip_whitespace();
// Parse shebang if present
if self.current_token_is(&TokenKind::Shebang(String::new())) {
if let Some(token) = self.advance() {
if let TokenKind::Shebang(content) = &token.kind {
ruleset = ruleset.with_shebang(content.clone());
}
}
self.skip_whitespace();
}
// Parse top-level statements
while !self.is_at_end() {
self.skip_whitespace();
if self.is_at_end() {
break;
}
match self.peek().map(|t| &t.kind) {
Some(TokenKind::Include) => {
let include = self.parse_include()?;
ruleset = ruleset.add_include(include);
}
Some(TokenKind::Define) => {
let define = self.parse_define()?;
ruleset = ruleset.add_define(define);
}
Some(TokenKind::Table) => {
let table = self.parse_table()?;
ruleset = ruleset.add_table(table);
}
Some(TokenKind::CommentLine(_)) => {
if let Some(token) = self.advance() {
if let TokenKind::CommentLine(comment_text) = &token.kind {
ruleset = ruleset.add_comment(comment_text.clone());
}
}
}
Some(TokenKind::Newline) => {
self.advance();
}
_ => {
return Err(ParseError::UnexpectedToken {
line: 1,
column: 1,
expected: "include, define, table, or comment".to_string(),
found: self
.peek()
.map(|t| t.text.clone())
.unwrap_or("EOF".to_string()),
});
}
}
}
Ok(ruleset)
}
/// Parse the entire ruleset with error recovery
pub fn parse_with_errors(source: &str) -> (Option<Ruleset>, Vec<ParseError>) {
let mut lexer = crate::lexer::NftablesLexer::new(source);
let tokens = lexer.tokenize_with_errors(); // Use the error-recovery tokenizer
let mut parser = Self::new(tokens);
let mut errors = Vec::new();
match parser.parse() {
Ok(ruleset) => (Some(ruleset), errors),
Err(err) => {
errors.push(err);
(None, errors)
}
}
}
/// Parse an include statement
fn parse_include(&mut self) -> ParseResult<Include> {
self.consume(TokenKind::Include, "Expected 'include'")?;
let path = match self.advance() {
Some(token) if matches!(token.kind, TokenKind::StringLiteral(_)) => match &token.kind {
TokenKind::StringLiteral(s) => s.clone(),
_ => unreachable!(),
},
None => {
return Err(ParseError::MissingToken {
expected: "string literal after 'include'".to_string(),
});
}
Some(token) => {
return Err(ParseError::UnexpectedToken {
line: 1,
column: 1,
expected: "string literal".to_string(),
found: token.text.clone(),
});
}
};
Ok(Include { path })
}
/// Parse a define statement
fn parse_define(&mut self) -> ParseResult<Define> {
self.consume(TokenKind::Define, "Expected 'define'")?;
let name = match self.advance() {
Some(token) if matches!(token.kind, TokenKind::Identifier(_)) => match &token.kind {
TokenKind::Identifier(s) => s.clone(),
_ => unreachable!(),
},
None => {
return Err(ParseError::MissingToken {
expected: "identifier after 'define'".to_string(),
});
}
Some(token) => {
return Err(ParseError::UnexpectedToken {
line: 1,
column: 1,
expected: "identifier".to_string(),
found: token.text.clone(),
});
}
};
self.consume(TokenKind::Assign, "Expected '=' after define name")?;
let value = self.parse_expression()?;
Ok(Define { name, value })
}
/// Parse a table definition
fn parse_table(&mut self) -> Result<Table> {
self.consume(TokenKind::Table, "Expected 'table'")?;
let family = self.parse_family()?;
let name = self.parse_identifier_or_keyword()?;
// Set parse context
self.context.in_table = true;
self.context.current_table = Some(name.clone());
let mut table = Table::new(family, name);
self.consume(TokenKind::LeftBrace, "Expected '{' after table name")?;
self.skip_whitespace();
while !self.current_token_is(&TokenKind::RightBrace) && !self.is_at_end() {
match self.peek().map(|t| &t.kind) {
Some(TokenKind::Chain) => {
let chain = self.parse_chain()?;
table = table.add_chain(chain);
}
Some(TokenKind::CommentLine(_)) => {
self.advance();
}
Some(TokenKind::Newline) => {
self.advance();
}
_ => {
return Err(ParseError::SemanticError {
message: format!(
"Unexpected token in table: {}",
self.peek().map(|t| t.text.as_str()).unwrap_or("EOF")
),
}
.into());
}
}
self.skip_whitespace();
}
self.consume(TokenKind::RightBrace, "Expected '}' to close table")?;
// Reset parse context
self.context.in_table = false;
self.context.current_table = None;
Ok(table)
}
/// Parse a chain definition
fn parse_chain(&mut self) -> Result<Chain> {
self.consume(TokenKind::Chain, "Expected 'chain'")?;
let name = self.parse_identifier_or_keyword()?;
// Set parse context
self.context.in_chain = true;
self.context.current_chain = Some(name.clone());
let mut chain = Chain::new(name);
self.consume(TokenKind::LeftBrace, "Expected '{' after chain name")?;
self.skip_whitespace();
// Parse chain properties and rules
while !self.current_token_is(&TokenKind::RightBrace) && !self.is_at_end() {
match self.peek().map(|t| &t.kind) {
Some(TokenKind::Type) => {
self.advance(); // consume 'type'
let chain_type = self.parse_chain_type()?;
chain = chain.with_type(chain_type);
if self.current_token_is(&TokenKind::Hook) {
self.advance(); // consume 'hook'
let hook = self.parse_hook()?;
chain = chain.with_hook(hook);
}
if self.current_token_is(&TokenKind::Priority) {
self.advance(); // consume 'priority'
let priority = self.parse_signed_number()? as i32;
chain = chain.with_priority(priority);
}
// Check for device specification (for netdev family)
if self.current_token_is(&TokenKind::Identifier("device".to_string())) {
self.advance(); // consume 'device'
let device = self.parse_string_or_identifier()?;
chain = chain.with_device(device);
}
self.consume(TokenKind::Semicolon, "Expected ';' after chain properties")?;
if self.current_token_is(&TokenKind::Policy) {
self.advance(); // consume 'policy'
let policy = self.parse_policy()?;
chain = chain.with_policy(policy);
}
self.consume(TokenKind::Semicolon, "Expected ';' after policy")?;
}
Some(TokenKind::CommentLine(_)) => {
self.advance();
}
Some(TokenKind::Newline) => {
self.advance();
}
_ => {
// Parse rule
let rule = self.parse_rule()?;
chain = chain.add_rule(rule);
}
}
self.skip_whitespace();
}
self.consume(TokenKind::RightBrace, "Expected '}' to close chain")?;
// Reset parse context
self.context.in_chain = false;
self.context.current_chain = None;
Ok(chain)
}
/// Parse a rule
fn parse_rule(&mut self) -> Result<Rule> {
// Set parse context
self.context.in_rule = true;
let mut expressions = Vec::new();
let mut action = None;
// Parse expressions and action
while !self.current_token_is(&TokenKind::Newline)
&& !self.current_token_is(&TokenKind::RightBrace)
&& !self.is_at_end()
{
// Check for actions first
match self.peek().map(|t| &t.kind) {
Some(TokenKind::Accept) => {
self.advance();
action = Some(Action::Accept);
break;
}
Some(TokenKind::Drop) => {
self.advance();
action = Some(Action::Drop);
break;
}
Some(TokenKind::Reject) => {
self.advance();
action = Some(Action::Reject);
break;
}
Some(TokenKind::Return) => {
self.advance();
action = Some(Action::Return);
break;
}
Some(TokenKind::Jump) => {
self.advance();
let target = self.parse_identifier()?;
action = Some(Action::Jump(target));
break;
}
Some(TokenKind::Goto) => {
self.advance();
let target = self.parse_identifier()?;
action = Some(Action::Goto(target));
break;
}
Some(TokenKind::Continue) => {
self.advance();
action = Some(Action::Continue);
break;
}
Some(TokenKind::Log) => {
self.advance();
// Parse optional log prefix and level
let mut prefix = None;
let mut level = None;
// Check for "prefix" keyword
if self.current_token_is(&TokenKind::Identifier("prefix".to_string())) {
self.advance();
if let Some(TokenKind::StringLiteral(p)) = self.peek().map(|t| &t.kind) {
prefix = Some(p.clone());
self.advance();
}
}
// Check for "level" keyword
if self.current_token_is(&TokenKind::Identifier("level".to_string())) {
self.advance();
if let Some(TokenKind::Identifier(l)) = self.peek().map(|t| &t.kind) {
level = Some(l.clone());
self.advance();
}
}
action = Some(Action::Log { prefix, level });
break;
}
Some(TokenKind::Comment) => {
self.advance();
if let Some(TokenKind::StringLiteral(text)) = self.peek().map(|t| &t.kind) {
let text = text.clone();
self.advance();
action = Some(Action::Comment(text));
break;
} else {
return Err(ParseError::InvalidStatement {
message: "Expected string literal after 'comment'".to_string(),
}
.into());
}
}
_ => {
let expr = self.parse_expression()?;
expressions.push(expr);
}
}
}
let action = action.unwrap_or(Action::Accept);
// Reset parse context
self.context.in_rule = false;
let mut rule = Rule::new(expressions, action);
// Check if we need to assign a handle (for demonstration purposes)
// In a real implementation, this might come from the source or be generated
if self.context.in_chain && rule.expressions.len() > 2 {
// Add handle for complex rules (arbitrary example)
rule = rule.with_handle(self.current.try_into().unwrap()); // Use current position as handle
}
Ok(rule)
}
/// Parse an expression with operator precedence
fn parse_expression(&mut self) -> Result<Expression> {
self.parse_comparison_expression()
}
/// Parse comparison expressions (==, !=, <, <=, >, >=)
fn parse_comparison_expression(&mut self) -> Result<Expression> {
let mut expr = self.parse_range_expression()?;
while let Some(token) = self.peek() {
let operator = match &token.kind {
TokenKind::Eq => BinaryOperator::Eq,
TokenKind::Ne => BinaryOperator::Ne,
TokenKind::Lt => BinaryOperator::Lt,
TokenKind::Le => BinaryOperator::Le,
TokenKind::Gt => BinaryOperator::Gt,
TokenKind::Ge => BinaryOperator::Ge,
_ => break,
};
self.advance(); // consume operator
let right = self.parse_range_expression()?;
expr = Expression::Binary {
left: Box::new(expr),
operator,
right: Box::new(right),
};
}
Ok(expr)
}
/// Parse range expressions (e.g., 1-100, 192.168.1.0-192.168.1.255)
fn parse_range_expression(&mut self) -> Result<Expression> {
let start = self.parse_primary_expression()?;
// Check for range operator (dash/minus)
if self.current_token_is(&TokenKind::Dash) {
self.advance(); // consume '-'
let end = self.parse_primary_expression()?;
Ok(Expression::Range {
start: Box::new(start),
end: Box::new(end),
})
} else {
Ok(start)
}
}
/// Parse primary expressions (literals, identifiers, etc.)
fn parse_primary_expression(&mut self) -> Result<Expression> {
match self.peek().map(|t| &t.kind) {
// Connection tracking
Some(TokenKind::Ct) => {
self.advance(); // consume 'ct'
let field = self.parse_identifier_or_keyword()?;
let value = Box::new(self.parse_ct_value()?);
Ok(Expression::ConnTrack { field, value })
}
// Interface matching
Some(TokenKind::Iifname) => {
self.advance(); // consume 'iifname'
let interface_name = self.parse_string_or_identifier()?;
Ok(Expression::Interface {
direction: InterfaceDirection::Input,
name: interface_name,
})
}
Some(TokenKind::Oifname) => {
self.advance(); // consume 'oifname'
let interface_name = self.parse_string_or_identifier()?;
Ok(Expression::Interface {
direction: InterfaceDirection::Output,
name: interface_name,
})
}
// Port matching
Some(TokenKind::Sport) => {
self.advance(); // consume 'sport'
let value = Box::new(self.parse_expression()?);
Ok(Expression::Port {
direction: PortDirection::Source,
value,
})
}
Some(TokenKind::Dport) => {
self.advance(); // consume 'dport'
let value = Box::new(self.parse_expression()?);
Ok(Expression::Port {
direction: PortDirection::Destination,
value,
})
}
// Address matching
Some(TokenKind::Saddr) => {
self.advance(); // consume 'saddr'
let value = Box::new(self.parse_expression()?);
Ok(Expression::Address {
direction: AddressDirection::Source,
value,
})
}
Some(TokenKind::Daddr) => {
self.advance(); // consume 'daddr'
let value = Box::new(self.parse_expression()?);
Ok(Expression::Address {
direction: AddressDirection::Destination,
value,
})
}
// Protocol keywords as expressions
Some(TokenKind::Ip) => {
self.advance();
// Check if followed by 'protocol', 'saddr', or 'daddr'
if self.current_token_is(&TokenKind::Protocol) {
self.advance(); // consume 'protocol'
let protocol = self.parse_identifier_or_keyword()?;
Ok(Expression::Protocol(protocol))
} else if self.current_token_is(&TokenKind::Saddr) {
self.advance(); // consume 'saddr'
let value = Box::new(self.parse_expression()?);
Ok(Expression::Address {
direction: AddressDirection::Source,
value,
})
} else if self.current_token_is(&TokenKind::Daddr) {
self.advance(); // consume 'daddr'
let value = Box::new(self.parse_expression()?);
Ok(Expression::Address {
direction: AddressDirection::Destination,
value,
})
} else {
Ok(Expression::Identifier("ip".to_string()))
}
}
Some(TokenKind::Ip6) => {
self.advance();
// Check if followed by 'nexthdr', 'saddr', or 'daddr'
if self.current_token_is(&TokenKind::Nexthdr) {
self.advance(); // consume 'nexthdr'
let protocol = self.parse_identifier_or_keyword()?;
Ok(Expression::Protocol(protocol))
} else if self.current_token_is(&TokenKind::Saddr) {
self.advance(); // consume 'saddr'
let value = Box::new(self.parse_expression()?);
Ok(Expression::Address {
direction: AddressDirection::Source,
value,
})
} else if self.current_token_is(&TokenKind::Daddr) {
self.advance(); // consume 'daddr'
let value = Box::new(self.parse_expression()?);
Ok(Expression::Address {
direction: AddressDirection::Destination,
value,
})
} else {
Ok(Expression::Identifier("ip6".to_string()))
}
}
Some(TokenKind::Tcp) => {
self.advance();
Ok(Expression::Protocol("tcp".to_string()))
}
Some(TokenKind::Udp) => {
self.advance();
Ok(Expression::Protocol("udp".to_string()))
}
Some(TokenKind::Icmp) => {
self.advance();
Ok(Expression::Protocol("icmp".to_string()))
}
Some(TokenKind::Icmpv6) => {
self.advance();
Ok(Expression::Protocol("icmpv6".to_string()))
}
// Connection states as expressions
Some(TokenKind::Established) => {
self.advance();
Ok(Expression::Identifier("established".to_string()))
}
Some(TokenKind::Related) => {
self.advance();
Ok(Expression::Identifier("related".to_string()))
}
Some(TokenKind::Invalid) => {
self.advance();
Ok(Expression::Identifier("invalid".to_string()))
}
Some(TokenKind::New) => {
self.advance();
Ok(Expression::Identifier("new".to_string()))
}
Some(TokenKind::Identifier(name)) => {
let name = name.clone();
self.advance();
// Check for special identifier patterns
match name.as_str() {
"ip" | "ip6" | "tcp" | "udp" | "icmp" | "icmpv6" => {
if self.current_token_is(&TokenKind::Protocol) {
self.advance(); // consume 'protocol'
let protocol = self.parse_identifier()?;
Ok(Expression::Protocol(protocol))
} else {
Ok(Expression::Identifier(name))
}
}
_ => Ok(Expression::Identifier(name)),
}
}
Some(TokenKind::StringLiteral(text)) => {
let text = text.clone();
self.advance();
Ok(Expression::String(text))
}
Some(TokenKind::NumberLiteral(_)) => {
let num = self.parse_number()?;
// Check for rate expression (e.g., 10/minute)
if self.current_token_is(&TokenKind::Slash) {
self.advance(); // consume '/'
if let Some(token) = self.peek() {
if matches!(token.kind, TokenKind::Identifier(_)) {
let unit = self.advance().unwrap().text.clone();
Ok(Expression::String(format!("{}/{}", num, unit)))
} else {
Err(ParseError::InvalidExpression {
message: "Expected identifier after '/' in rate expression"
.to_string(),
}
.into())
}
} else {
Err(ParseError::InvalidExpression {
message: "Expected identifier after '/' in rate expression".to_string(),
}
.into())
}
} else {
Ok(Expression::Number(num))
}
}
Some(TokenKind::IpAddress(_)) => {
let addr = self.advance().unwrap().text.clone();
// Check for CIDR notation (e.g., 192.168.1.0/24)
if self.current_token_is(&TokenKind::Slash) {
self.advance(); // consume '/'
if let Some(token) = self.peek() {
if matches!(token.kind, TokenKind::NumberLiteral(_)) {
let prefix_len = self.advance().unwrap().text.clone();
Ok(Expression::IpAddress(format!("{}/{}", addr, prefix_len)))
} else {
Err(ParseError::InvalidExpression {
message: "Expected number after '/' in CIDR notation".to_string(),
}
.into())
}
} else {
Err(ParseError::InvalidExpression {
message: "Expected number after '/' in CIDR notation".to_string(),
}
.into())
}
// Check for port specification (e.g., 192.168.1.100:80)
} else if self.current_token_is(&TokenKind::Colon) {
self.advance(); // consume ':'
if let Some(token) = self.peek() {
if matches!(token.kind, TokenKind::NumberLiteral(_)) {
let port = self.advance().unwrap().text.clone();
Ok(Expression::String(format!("{}:{}", addr, port)))
} else {
Err(ParseError::InvalidExpression {
message: "Expected number after ':' in address:port specification"
.to_string(),
}
.into())
}
} else {
Err(ParseError::InvalidExpression {
message: "Expected number after ':' in address:port specification"
.to_string(),
}
.into())
}
} else {
Ok(Expression::IpAddress(addr))
}
}
Some(TokenKind::Ipv6Address(_)) => {
let addr = self.advance().unwrap().text.clone();
Ok(Expression::Ipv6Address(addr))
}
Some(TokenKind::MacAddress(_)) => {
let addr = self.advance().unwrap().text.clone();
Ok(Expression::MacAddress(addr))
}
Some(TokenKind::LeftBrace) => {
self.advance(); // consume '{'
let mut elements = Vec::new();
while !self.current_token_is(&TokenKind::RightBrace) && !self.is_at_end() {
if self.current_token_is(&TokenKind::Comma) {
self.advance();
continue;
}
if self.current_token_is(&TokenKind::Newline) {
self.advance();
continue;
}
let element = self.parse_expression()?;
elements.push(element);
}
self.consume(TokenKind::RightBrace, "Expected '}' to close set")?;
Ok(Expression::Set(elements))
}
_ => Err(ParseError::InvalidExpression {
message: format!(
"Unexpected token in expression: {}",
self.peek().map(|t| t.text.as_str()).unwrap_or("EOF")
),
}
.into()),
}
}
/// Parse connection tracking values (handles comma-separated lists)
fn parse_ct_value(&mut self) -> Result<Expression> {
let mut values = Vec::new();
loop {
let value = match self.peek().map(|t| &t.kind) {
Some(TokenKind::Established) => {
self.advance();
Expression::Identifier("established".to_string())
}
Some(TokenKind::Related) => {
self.advance();
Expression::Identifier("related".to_string())
}
Some(TokenKind::Invalid) => {
self.advance();
Expression::Identifier("invalid".to_string())
}
Some(TokenKind::New) => {
self.advance();
Expression::Identifier("new".to_string())
}
Some(TokenKind::Identifier(name)) => {
let name = name.clone();
self.advance();
Expression::Identifier(name)
}
_ => return self.parse_expression(),
};
values.push(value);
// Check if there's a comma for more values
if self.current_token_is(&TokenKind::Comma) {
self.advance(); // consume comma
continue;
} else {
break;
}
}
if values.len() == 1 {
Ok(values.into_iter().next().unwrap())
} else {
Ok(Expression::Set(values))
}
}
/// Helper methods
fn parse_family(&mut self) -> ParseResult<Family> {
match self.advance() {
Some(token) => match token.kind {
TokenKind::Ip => Ok(Family::Ip),
TokenKind::Ip6 => Ok(Family::Ip6),
TokenKind::Inet => Ok(Family::Inet),
TokenKind::Arp => Ok(Family::Arp),
TokenKind::Bridge => Ok(Family::Bridge),
TokenKind::Netdev => Ok(Family::Netdev),
_ => {
let token_clone = token.clone();
Err(self.unexpected_token_error_with_token(
"family (ip, ip6, inet, arp, bridge, netdev)".to_string(),
&token_clone,
))
}
},
None => Err(ParseError::MissingToken {
expected: "family".to_string(),
}),
}
}
fn parse_chain_type(&mut self) -> ParseResult<ChainType> {
match self.advance() {
Some(token) => match token.kind {
TokenKind::Filter => Ok(ChainType::Filter),
TokenKind::Nat => Ok(ChainType::Nat),
TokenKind::Route => Ok(ChainType::Route),
_ => {
let token_clone = token.clone();
Err(self.unexpected_token_error_with_token(
"chain type (filter, nat, route)".to_string(),
&token_clone,
))
}
},
None => Err(ParseError::MissingToken {
expected: "chain type".to_string(),
}),
}
}
fn parse_hook(&mut self) -> ParseResult<Hook> {
match self.advance() {
Some(token) => match token.kind {
TokenKind::Input => Ok(Hook::Input),
TokenKind::Output => Ok(Hook::Output),
TokenKind::Forward => Ok(Hook::Forward),
TokenKind::Prerouting => Ok(Hook::Prerouting),
TokenKind::Postrouting => Ok(Hook::Postrouting),
_ => {
let token_clone = token.clone();
Err(self.unexpected_token_error_with_token(
"hook (input, output, forward, prerouting, postrouting)".to_string(),
&token_clone,
))
}
},
None => Err(ParseError::MissingToken {
expected: "hook".to_string(),
}),
}
}
fn parse_policy(&mut self) -> Result<Policy> {
match self.advance() {
Some(token) => match token.kind {
TokenKind::Accept => Ok(Policy::Accept),
TokenKind::Drop => Ok(Policy::Drop),
_ => Err(anyhow!(
"Expected policy (accept, drop), got: {}",
token.text
)),
},
None => Err(anyhow!("Expected policy")),
}
}
fn parse_identifier(&mut self) -> Result<String> {
match self.advance() {
Some(token) if matches!(token.kind, TokenKind::Identifier(_)) => match &token.kind {
TokenKind::Identifier(s) => Ok(s.clone()),
_ => unreachable!(),
},
Some(token) => Err(anyhow!("Expected identifier, got: {}", token.text)),
None => Err(anyhow!("Expected identifier")),
}
}
fn parse_identifier_or_keyword(&mut self) -> Result<String> {
match self.advance() {
Some(token) => {
// Accept identifiers or keywords that can be used as identifiers
match &token.kind {
TokenKind::Identifier(s) => Ok(s.clone()),
// Allow keywords to be used as identifiers in certain contexts
TokenKind::Filter
| TokenKind::Nat
| TokenKind::Route
| TokenKind::Input
| TokenKind::Output
| TokenKind::Forward
| TokenKind::Prerouting
| TokenKind::Postrouting
| TokenKind::Accept
| TokenKind::Drop
| TokenKind::Reject
| TokenKind::State
| TokenKind::Ct
| TokenKind::Type
| TokenKind::Hook
| TokenKind::Priority
| TokenKind::Policy
| TokenKind::Tcp
| TokenKind::Udp
| TokenKind::Icmp
| TokenKind::Icmpv6
| TokenKind::Ip
| TokenKind::Ip6 => Ok(token.text.clone()),
_ => Err(anyhow!(
"Expected identifier or keyword, got: {}",
token.text
)),
}
}
None => Err(anyhow!("Expected identifier or keyword")),
}
}
fn parse_string_or_identifier(&mut self) -> Result<String> {
match self.advance() {
Some(token)
if matches!(
token.kind,
TokenKind::Identifier(_) | TokenKind::StringLiteral(_)
) =>
{
match &token.kind {
TokenKind::Identifier(s) => Ok(s.clone()),
TokenKind::StringLiteral(s) => Ok(s.clone()),
_ => unreachable!(),
}
}
Some(token) => Err(anyhow!(
"Expected string or identifier, got: {}",
token.text
)),
None => Err(anyhow!("Expected string or identifier")),
}
}
fn parse_number(&mut self) -> Result<u64> {
match self.advance() {
Some(token) if matches!(token.kind, TokenKind::NumberLiteral(_)) => match &token.kind {
TokenKind::NumberLiteral(n) => Ok(*n),
_ => unreachable!(),
},
Some(token) => Err(anyhow!("Expected number, got: {}", token.text)),
None => Err(anyhow!("Expected number")),
}
}
fn parse_signed_number(&mut self) -> Result<i64> {
// Check if we have a dash (negative sign)
if self.current_token_is(&TokenKind::Dash) {
self.advance(); // consume the dash
match self.advance() {
Some(token) if matches!(token.kind, TokenKind::NumberLiteral(_)) => {
match &token.kind {
TokenKind::NumberLiteral(n) => Ok(-(*n as i64)),
_ => unreachable!(),
}
}
Some(token) => Err(anyhow!("Expected number after '-', got: {}", token.text)),
None => Err(anyhow!("Expected number after '-'")),
}
} else {
// No dash, parse as positive number
match self.advance() {
Some(token) if matches!(token.kind, TokenKind::NumberLiteral(_)) => {
match &token.kind {
TokenKind::NumberLiteral(n) => Ok(*n as i64),
_ => unreachable!(),
}
}
Some(token) => Err(anyhow!("Expected number, got: {}", token.text)),
None => Err(anyhow!("Expected number")),
}
}
}
// Navigation and utility methods
fn advance(&mut self) -> Option<&Token> {
if !self.is_at_end() {
self.current += 1;
}
self.previous()
}
fn is_at_end(&self) -> bool {
self.current >= self.tokens.len()
}
fn peek(&self) -> Option<&Token> {
self.tokens.get(self.current)
}
fn previous(&self) -> Option<&Token> {
if self.current > 0 {
self.tokens.get(self.current - 1)
} else {
None
}
}
fn current_token_is(&self, kind: &TokenKind) -> bool {
match self.peek() {
Some(token) => std::mem::discriminant(&token.kind) == std::mem::discriminant(kind),
None => false,
}
}
fn consume(&mut self, expected: TokenKind, message: &str) -> Result<()> {
if self.current_token_is(&expected) {
self.advance();
Ok(())
} else {
Err(anyhow!("{}", message))
}
}
fn skip_whitespace(&mut self) {
while let Some(token) = self.peek() {
match token.kind {
TokenKind::Newline => {
self.advance();
}
_ => break,
}
}
}
fn unexpected_token_error_with_token(&self, expected: String, token: &Token) -> ParseError {
ParseError::UnexpectedToken {
line: 1, // TODO: Calculate line from range
column: token.range.start().into(),
expected,
found: token.text.clone(),
}
}
}