treewide: fmt
This commit is contained in:
parent
c4beb3e65f
commit
10a525b2e7
5 changed files with 326 additions and 206 deletions
255
src/parser.rs
255
src/parser.rs
|
@ -1,12 +1,14 @@
|
|||
use crate::ast::*;
|
||||
use crate::lexer::{LexError, Token, TokenKind};
|
||||
use anyhow::{anyhow, Result};
|
||||
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}'")]
|
||||
#[error(
|
||||
"Unexpected token at line {line}, column {column}: expected {expected}, found '{found}'"
|
||||
)]
|
||||
UnexpectedToken {
|
||||
line: usize,
|
||||
column: usize,
|
||||
|
@ -123,7 +125,10 @@ impl Parser {
|
|||
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()),
|
||||
found: self
|
||||
.peek()
|
||||
.map(|t| t.text.clone())
|
||||
.unwrap_or("EOF".to_string()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -154,21 +159,23 @@ impl Parser {
|
|||
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!(),
|
||||
}
|
||||
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(),
|
||||
});
|
||||
}
|
||||
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 })
|
||||
|
@ -179,21 +186,23 @@ impl Parser {
|
|||
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!(),
|
||||
}
|
||||
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(),
|
||||
});
|
||||
}
|
||||
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")?;
|
||||
|
@ -232,10 +241,14 @@ impl Parser {
|
|||
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());
|
||||
_ => {
|
||||
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();
|
||||
|
@ -337,8 +350,8 @@ impl Parser {
|
|||
// Parse expressions and action
|
||||
while !self.current_token_is(&TokenKind::Newline)
|
||||
&& !self.current_token_is(&TokenKind::RightBrace)
|
||||
&& !self.is_at_end() {
|
||||
|
||||
&& !self.is_at_end()
|
||||
{
|
||||
// Check for actions first
|
||||
match self.peek().map(|t| &t.kind) {
|
||||
Some(TokenKind::Accept) => {
|
||||
|
@ -415,7 +428,8 @@ impl Parser {
|
|||
} else {
|
||||
return Err(ParseError::InvalidStatement {
|
||||
message: "Expected string literal after 'comment'".to_string(),
|
||||
}.into());
|
||||
}
|
||||
.into());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -451,16 +465,16 @@ impl Parser {
|
|||
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,
|
||||
};
|
||||
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()?;
|
||||
|
@ -664,19 +678,22 @@ impl Parser {
|
|||
// 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())
|
||||
}
|
||||
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())
|
||||
}
|
||||
.into())
|
||||
}
|
||||
} else {
|
||||
Ok(Expression::Number(num))
|
||||
|
@ -694,12 +711,14 @@ impl Parser {
|
|||
} else {
|
||||
Err(ParseError::InvalidExpression {
|
||||
message: "Expected number after '/' in CIDR notation".to_string(),
|
||||
}.into())
|
||||
}
|
||||
.into())
|
||||
}
|
||||
} else {
|
||||
Err(ParseError::InvalidExpression {
|
||||
message: "Expected number after '/' in CIDR notation".to_string(),
|
||||
}.into())
|
||||
}
|
||||
.into())
|
||||
}
|
||||
// Check for port specification (e.g., 192.168.1.100:80)
|
||||
} else if self.current_token_is(&TokenKind::Colon) {
|
||||
|
@ -710,13 +729,17 @@ impl Parser {
|
|||
Ok(Expression::String(format!("{}:{}", addr, port)))
|
||||
} else {
|
||||
Err(ParseError::InvalidExpression {
|
||||
message: "Expected number after ':' in address:port specification".to_string(),
|
||||
}.into())
|
||||
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())
|
||||
message: "Expected number after ':' in address:port specification"
|
||||
.to_string(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
} else {
|
||||
Ok(Expression::IpAddress(addr))
|
||||
|
@ -751,12 +774,13 @@ impl Parser {
|
|||
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())
|
||||
_ => Err(ParseError::InvalidExpression {
|
||||
message: format!(
|
||||
"Unexpected token in expression: {}",
|
||||
self.peek().map(|t| t.text.as_str()).unwrap_or("EOF")
|
||||
),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -822,12 +846,12 @@ impl Parser {
|
|||
let token_clone = token.clone();
|
||||
Err(self.unexpected_token_error_with_token(
|
||||
"family (ip, ip6, inet, arp, bridge, netdev)".to_string(),
|
||||
&token_clone
|
||||
&token_clone,
|
||||
))
|
||||
}
|
||||
},
|
||||
None => Err(ParseError::MissingToken {
|
||||
expected: "family".to_string()
|
||||
expected: "family".to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -842,12 +866,12 @@ impl Parser {
|
|||
let token_clone = token.clone();
|
||||
Err(self.unexpected_token_error_with_token(
|
||||
"chain type (filter, nat, route)".to_string(),
|
||||
&token_clone
|
||||
&token_clone,
|
||||
))
|
||||
}
|
||||
},
|
||||
None => Err(ParseError::MissingToken {
|
||||
expected: "chain type".to_string()
|
||||
expected: "chain type".to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -864,12 +888,12 @@ impl Parser {
|
|||
let token_clone = token.clone();
|
||||
Err(self.unexpected_token_error_with_token(
|
||||
"hook (input, output, forward, prerouting, postrouting)".to_string(),
|
||||
&token_clone
|
||||
&token_clone,
|
||||
))
|
||||
}
|
||||
},
|
||||
None => Err(ParseError::MissingToken {
|
||||
expected: "hook".to_string()
|
||||
expected: "hook".to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -879,7 +903,10 @@ impl Parser {
|
|||
Some(token) => match token.kind {
|
||||
TokenKind::Accept => Ok(Policy::Accept),
|
||||
TokenKind::Drop => Ok(Policy::Drop),
|
||||
_ => Err(anyhow!("Expected policy (accept, drop), got: {}", token.text)),
|
||||
_ => Err(anyhow!(
|
||||
"Expected policy (accept, drop), got: {}",
|
||||
token.text
|
||||
)),
|
||||
},
|
||||
None => Err(anyhow!("Expected policy")),
|
||||
}
|
||||
|
@ -887,12 +914,10 @@ impl Parser {
|
|||
|
||||
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) 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")),
|
||||
}
|
||||
|
@ -905,17 +930,33 @@ impl Parser {
|
|||
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)),
|
||||
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")),
|
||||
|
@ -924,26 +965,32 @@ impl Parser {
|
|||
|
||||
fn parse_string_or_identifier(&mut self) -> Result<String> {
|
||||
match self.advance() {
|
||||
Some(token) if matches!(token.kind, TokenKind::Identifier(_) | TokenKind::StringLiteral(_)) => {
|
||||
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)),
|
||||
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) 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")),
|
||||
}
|
||||
|
@ -1029,11 +1076,7 @@ impl Parser {
|
|||
}
|
||||
}
|
||||
|
||||
fn unexpected_token_error_with_token(
|
||||
&self,
|
||||
expected: String,
|
||||
token: &Token,
|
||||
) -> ParseError {
|
||||
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(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue