use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::net::IpAddr;
use core::str::FromStr;
use winnow::ModalResult as PResult;
use winnow::combinator::{alt, cut_err, opt, repeat, separated};
use winnow::error::{ContextError, ErrMode};
use winnow::prelude::*;
use winnow::token::any;
use crate::ast::*;
use crate::error::CompilerError;
use crate::lexer::Token;
pub(crate) mod field_apply;
pub(crate) mod fields;
pub(crate) mod finish;
pub(crate) mod ident;
pub(crate) mod l2;
pub(crate) mod l3;
pub(crate) mod l4;
pub(crate) mod l7;
pub(crate) mod l7_apply;
pub(crate) mod snmp {
pub(crate) use super::l7::snmp::*;
}
pub(crate) mod statement;
pub(crate) mod validation;
#[cfg(test)]
mod tests;
pub(crate) use fields::ParsedField;
pub(crate) use ident::{parse_any_str, parse_ident, parse_spanned_ident};
pub use ident::unescape_string;
pub(crate) use statement::parse_frame_statement;
pub use validation::validate_semantics;
pub type Stream<'i, 'a> = &'i [(Token<'a>, core::ops::Range<usize>, &'a str)];
pub(crate) fn tag<'i, 'a: 'i>(
expected: Token<'a>,
) -> impl Parser<Stream<'i, 'a>, (Token<'a>, core::ops::Range<usize>), ErrMode<ContextError>> {
any.verify_map(
move |(t, span, _): (Token<'a>, core::ops::Range<usize>, &'a str)| {
let matches = match (&t, &expected) {
(Token::Ident(a), Token::Ident(b)) => a.eq_ignore_ascii_case(b),
_ => t == expected,
};
if matches { Some((t, span)) } else { None }
},
)
}
pub(crate) fn parse_raw_string_lit<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<&'a str> {
let (t, _, _) = any
.verify(|(t, _, _)| matches!(t, Token::StringLit(_)))
.parse_next(input)?;
match t {
Token::StringLit(s) => Ok(s
.strip_prefix('"')
.and_then(|s| s.strip_suffix('"'))
.unwrap_or(s)),
_ => unreachable!(),
}
}
pub(crate) fn parse_string_lit<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<String> {
let raw = parse_raw_string_lit(input)?;
unescape_string(raw).map_err(|_| winnow::error::ErrMode::Cut(ContextError::new()))
}
pub(crate) fn parse_hex_payload<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<Vec<u8>> {
let raw = parse_raw_string_lit(input)?;
let clean: String = raw
.chars()
.filter(|c| !c.is_whitespace() && *c != ':')
.collect();
if !clean.len().is_multiple_of(2) {
return Err(winnow::error::ErrMode::Cut(ContextError::new()));
}
let mut bytes = Vec::with_capacity(clean.len() / 2);
for i in (0..clean.len()).step_by(2) {
let b = u8::from_str_radix(&clean[i..i + 2], 16)
.map_err(|_| winnow::error::ErrMode::Cut(ContextError::new()))?;
bytes.push(b);
}
Ok(bytes)
}
pub(crate) fn parse_str_val<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<&'a str> {
alt((parse_raw_string_lit, parse_ident)).parse_next(input)
}
pub(crate) fn parse_hex_bytes<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<Vec<u8>> {
alt((
parse_hex_payload,
parse_u64.map(|v| {
let b = v.to_be_bytes();
let leading = b.iter().take_while(|&&x| x == 0).count();
if leading == 8 {
alloc::vec![0]
} else {
b[leading..].to_vec()
}
}),
))
.parse_next(input)
}
pub(crate) fn parse_ip<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<IpAddr> {
any.verify_map(|(t, _, _)| match t {
Token::IpAddress(s) | Token::OidStr(s) => IpAddr::from_str(s).ok(),
_ => None,
})
.parse_next(input)
}
pub(crate) fn parse_mac<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<[u8; 6]> {
any.verify_map(|(t, _, _)| {
if let Token::MacAddress(s) = t {
let parts: Vec<&str> = s.split(':').collect();
if parts.len() == 6 {
let mut mac = [0u8; 6];
for i in 0..6 {
mac[i] = u8::from_str_radix(parts[i], 16).ok()?;
}
Some(mac)
} else {
None
}
} else {
None
}
})
.parse_next(input)
}
pub(crate) fn parse_u8<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<u8> {
any.verify_map(|(t, _, _)| {
if let Token::Number(s) = t {
if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
u8::from_str_radix(hex, 16).ok()
} else {
u8::from_str(s).ok()
}
} else {
None
}
})
.parse_next(input)
}
pub(crate) fn parse_u16<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<u16> {
any.verify_map(|(t, _, _)| {
if let Token::Number(s) = t {
if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
u16::from_str_radix(hex, 16).ok()
} else {
u16::from_str(s).ok()
}
} else {
None
}
})
.parse_next(input)
}
pub(crate) fn parse_spanned_u32<'i, 'a>(
input: &mut Stream<'i, 'a>,
) -> PResult<(u32, (usize, usize))> {
any.verify_map(|(t, span, _): (Token<'a>, core::ops::Range<usize>, &'a str)| {
if let Token::Number(s) = t {
let val = if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
u32::from_str_radix(hex, 16).ok()
} else {
u32::from_str(s).ok()
}?;
Some((val, (span.start, span.end - span.start)))
} else {
None
}
})
.parse_next(input)
}
pub(crate) fn parse_u32<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<u32> {
parse_spanned_u32.map(|(v, _)| v).parse_next(input)
}
pub(crate) fn parse_i32<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<i32> {
any.verify_map(|(t, _, _)| {
if let Token::Number(s) = t {
if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
i32::from_str_radix(hex, 16).ok()
} else {
i32::from_str(s).ok()
}
} else {
None
}
})
.parse_next(input)
}
pub(crate) fn parse_u64<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<u64> {
any.verify_map(|(t, _, _)| {
if let Token::Number(s) = t {
if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
u64::from_str_radix(hex, 16).ok()
} else {
u64::from_str(s).ok()
}
} else {
None
}
})
.parse_next(input)
}
pub(crate) fn parse_host_def<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<HostDef<'a>> {
let (_, host_kw_span) = tag(Token::Host).parse_next(input)?;
let name_spanned = cut_err(parse_spanned_ident).parse_next(input)?;
let _ = cut_err(tag(Token::LBrace)).parse_next(input)?;
let mut ip = None;
let mut mac = None;
let _: Vec<()> = repeat(
0..,
alt((
(tag(Token::Ip), cut_err(parse_ip)).map(|(_, i)| ip = Some(i)),
(tag(Token::Mac), cut_err(parse_mac)).map(|(_, m)| mac = Some(m)),
)),
)
.parse_next(input)?;
let (_, rbrace_span) = cut_err(tag(Token::RBrace)).parse_next(input)?;
if let Some(ip) = ip {
Ok(HostDef {
name: name_spanned.node,
ip,
mac,
span: (host_kw_span.start, rbrace_span.end - host_kw_span.start),
})
} else {
Err(winnow::error::ErrMode::Cut(ContextError::new()))
}
}
pub(crate) fn parse_direction<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<Direction> {
alt((
tag(Token::RightArrow).value(Direction::LeftToRight),
tag(Token::LeftArrow).value(Direction::RightToLeft),
))
.parse_next(input)
}
pub(crate) fn parse_flow_def<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<FlowDef<'a>> {
let (_, flow_kw_span) = tag(Token::Flow).parse_next(input)?;
let name = cut_err(parse_spanned_ident).parse_next(input)?;
let _ = cut_err(tag(Token::LParen)).parse_next(input)?;
let params: Vec<&'a str> =
cut_err(separated(0.., parse_ident, tag(Token::Comma))).parse_next(input)?;
let _ = cut_err(tag(Token::RParen)).parse_next(input)?;
let _ = cut_err(tag(Token::LBrace)).parse_next(input)?;
let statements: Vec<FrameStatement<'a>> =
cut_err(repeat(0.., parse_frame_statement)).parse_next(input)?;
let (_, rbrace_span) = cut_err(tag(Token::RBrace)).parse_next(input)?;
Ok(FlowDef {
name: name.node,
params,
statements,
span: (flow_kw_span.start, rbrace_span.end - flow_kw_span.start),
})
}
pub(crate) fn parse_argument<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<Argument<'a>> {
let var = parse_ident(input)?;
Ok(Argument::Variable(var))
}
pub(crate) fn parse_template_invocation<'i, 'a>(
input: &mut Stream<'i, 'a>,
) -> PResult<TemplateInvocation<'a>> {
let name_spanned = parse_spanned_ident(input)?;
let _ = tag(Token::LParen).parse_next(input)?;
let args: Vec<Argument<'a>> =
separated(0.., parse_argument, tag(Token::Comma)).parse_next(input)?;
let (_, rparen_span) = tag(Token::RParen).parse_next(input)?;
Ok(TemplateInvocation {
name: name_spanned.node,
args,
span: (
name_spanned.span.0,
rparen_span.end - name_spanned.span.0,
),
})
}
pub(crate) fn parse_run_statement<'i, 'a>(
input: &mut Stream<'i, 'a>,
) -> PResult<RunStatement<'a>> {
if opt(tag(Token::Loop)).parse_next(input)?.is_some() {
let count = cut_err(parse_u32).parse_next(input)?;
let _ = cut_err(tag(Token::LBrace)).parse_next(input)?;
let invocations: Vec<TemplateInvocation<'a>> =
cut_err(repeat(0.., parse_template_invocation)).parse_next(input)?;
let _ = cut_err(tag(Token::RBrace)).parse_next(input)?;
Ok(RunStatement::Loop(count, invocations))
} else {
let inv = parse_template_invocation(input)?;
Ok(RunStatement::Invocation(inv))
}
}
pub(crate) fn parse_compile_block<'i, 'a>(
input: &mut Stream<'i, 'a>,
) -> PResult<Vec<RunStatement<'a>>> {
let _ = tag(Token::Compile).parse_next(input)?;
let _ = cut_err(tag(Token::LBrace)).parse_next(input)?;
let statements: Vec<RunStatement<'a>> =
cut_err(repeat(0.., parse_run_statement)).parse_next(input)?;
let _ = cut_err(tag(Token::RBrace)).parse_next(input)?;
Ok(statements)
}
pub(crate) enum TopLevel<'a> {
Host(HostDef<'a>),
Flow(FlowDef<'a>),
Compile(Vec<RunStatement<'a>>),
}
pub(crate) fn parse_top_level<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<TopLevel<'a>> {
alt((
parse_host_def.map(TopLevel::Host),
parse_flow_def.map(TopLevel::Flow),
parse_compile_block.map(TopLevel::Compile),
))
.parse_next(input)
}
pub(crate) fn parse_all<'i, 'a>(input: &mut Stream<'i, 'a>) -> PResult<Program<'a>> {
let mut hosts = Vec::new();
let mut flows = Vec::new();
let mut compile_block = None;
let top_levels: Vec<TopLevel<'a>> = repeat(0.., parse_top_level).parse_next(input)?;
for tl in top_levels {
match tl {
TopLevel::Host(h) => hosts.push(h),
TopLevel::Flow(f) => flows.push(f),
TopLevel::Compile(c) => {
if compile_block.is_some() {
return Err(winnow::error::ErrMode::Cut(ContextError::new()));
}
compile_block = Some(c);
}
}
}
if let Some(compile_block) = compile_block {
Ok(Program {
hosts,
flows,
compile_block,
})
} else {
Err(winnow::error::ErrMode::Cut(ContextError::new()))
}
}
pub fn parse_program<'a>(input_str: &'a str) -> Result<Program<'a>, CompilerError> {
let raw_tokens = crate::lexer::lex(input_str)?;
let tokens: Vec<(Token<'a>, core::ops::Range<usize>, &'a str)> = raw_tokens
.into_iter()
.map(|(t, span)| {
let slice = &input_str[span.clone()];
(t, span, slice)
})
.collect();
let mut stream: Stream<'_, 'a> = &tokens;
let program = match parse_all.parse_next(&mut stream) {
Ok(program) => {
if !stream.is_empty() {
let span = stream[0].1.clone();
return Err(CompilerError::ParseError {
message: "Unexpected tokens at end of file".to_string(),
src: input_str.to_string(),
span: (span.start, span.end - span.start),
});
}
program
}
Err(e) => {
let offset = if stream.is_empty() {
input_str.len()..input_str.len()
} else {
stream[0].1.clone()
};
return Err(CompilerError::ParseError {
message: alloc::format!("Syntax error: {:?}", e),
src: input_str.to_string(),
span: (offset.start, offset.end - offset.start),
});
}
};
validate_semantics(&program, input_str)?;
Ok(program)
}