use std::collections::HashSet;
use std::path::{Path, PathBuf};
use crate::error::{Error, ErrorKind, Span};
use crate::format_parser::parse_format_string;
use crate::types::*;
pub fn parse(source: &str, filename: &str) -> Result<DecoderDef, Vec<Error>> {
let mut parser = Parser::new(source, filename);
parser.parse_file()
}
pub fn parse_file(path: &Path) -> Result<DecoderDef, Vec<Error>> {
let mut include_guard = HashSet::new();
parse_file_with_includes(path, &mut include_guard)
}
pub fn parse_file_with_deps(path: &Path) -> Result<(DecoderDef, Vec<PathBuf>), Vec<Error>> {
let mut include_guard = HashSet::new();
let def = parse_file_with_includes(path, &mut include_guard)?;
let deps: Vec<PathBuf> = include_guard.into_iter().collect();
Ok((def, deps))
}
fn parse_file_with_includes(
path: &Path,
include_guard: &mut HashSet<PathBuf>,
) -> Result<DecoderDef, Vec<Error>> {
let canonical = path.canonicalize().map_err(|_| {
vec![Error::new(
ErrorKind::IncludeNotFound(path.display().to_string()),
Span::new(&path.display().to_string(), 1, 1, 0),
)]
})?;
if !include_guard.insert(canonical.clone()) {
return Err(vec![Error::new(
ErrorKind::CircularInclude(path.display().to_string()),
Span::new(&path.display().to_string(), 1, 1, 0),
)]);
}
let source = std::fs::read_to_string(&canonical).map_err(|_| {
vec![Error::new(
ErrorKind::IncludeNotFound(path.display().to_string()),
Span::new(&path.display().to_string(), 1, 1, 0),
)]
})?;
let filename = path
.file_name()
.and_then(|f| f.to_str())
.unwrap_or("unknown");
let base_dir = canonical.parent().unwrap_or(Path::new("."));
let mut included_sub_decoders = Vec::new();
let mut included_maps = Vec::new();
let mut included_type_aliases = Vec::new();
for line in source.lines() {
let trimmed = line.trim();
if let Some(rest) = trimmed.strip_prefix("include ") {
let rest = rest.trim();
if let Some(inc_path_str) = extract_quoted_string(rest) {
let inc_path = base_dir.join(inc_path_str);
let inc_def = parse_file_with_includes(&inc_path, include_guard)?;
included_sub_decoders.extend(inc_def.sub_decoders);
included_maps.extend(inc_def.maps);
included_type_aliases.extend(inc_def.type_aliases);
}
}
}
let mut def = parse(&source, filename)?;
def.sub_decoders.extend(included_sub_decoders);
def.maps.extend(included_maps);
def.type_aliases.extend(included_type_aliases);
Ok(def)
}
struct Parser<'a> {
filename: String,
lines: Vec<&'a str>,
line_idx: usize,
errors: Vec<Error>,
}
impl<'a> Parser<'a> {
fn new(source: &'a str, filename: &str) -> Self {
let lines: Vec<&str> = source.lines().collect();
Parser {
filename: filename.to_string(),
lines,
line_idx: 0,
errors: Vec::new(),
}
}
fn span(&self, col: usize, len: usize) -> Span {
Span::new(&self.filename, self.line_idx + 1, col + 1, len)
}
fn advance(&mut self) {
self.line_idx += 1;
}
fn parse_file(&mut self) -> Result<DecoderDef, Vec<Error>> {
let mut config: Option<DecoderConfig> = None;
let mut type_aliases = Vec::new();
let mut maps = Vec::new();
let mut instructions = Vec::new();
let mut sub_decoders = Vec::new();
let mut current_subdecoder: Option<usize> = None;
while self.line_idx < self.lines.len() {
let line = self.lines[self.line_idx];
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
self.advance();
continue;
}
if trimmed.starts_with("include ") {
self.advance();
continue;
}
if trimmed.starts_with("decoder ") {
current_subdecoder = None;
match self.parse_decoder_block() {
Ok(cfg) => config = Some(cfg),
Err(e) => self.errors.push(e),
}
} else if trimmed.starts_with("subdecoder ") {
match self.parse_subdecoder_block() {
Ok(sd) => {
sub_decoders.push(sd);
current_subdecoder = Some(sub_decoders.len() - 1);
}
Err(e) => self.errors.push(e),
}
} else if trimmed.starts_with("type ") {
match self.parse_type_alias(trimmed) {
Ok(ta) => type_aliases.push(ta),
Err(e) => self.errors.push(e),
}
self.advance();
} else if trimmed.starts_with("map ") {
match self.parse_map_block() {
Ok(map) => {
if let Some(sd_idx) = current_subdecoder {
sub_decoders[sd_idx].maps.push(map);
} else {
maps.push(map);
}
}
Err(e) => self.errors.push(e),
}
} else {
match self.parse_instruction(trimmed) {
Ok(instr) => {
if let Some(sd_idx) = current_subdecoder {
let sub_instr_name = instr.name.clone();
let sub_instr_segments = instr.segments.clone();
let sub_instr_span = instr.span.clone();
self.advance();
let fragments = self.parse_fragment_lines();
sub_decoders[sd_idx].instructions.push(SubInstructionDef {
name: sub_instr_name,
segments: sub_instr_segments,
fragments,
span: sub_instr_span,
});
} else {
instructions.push(instr);
self.advance();
let format_lines = self.parse_format_lines();
if let Some(last) = instructions.last_mut() {
last.format_lines = format_lines;
}
}
}
Err(e) => {
self.errors.push(e);
self.advance();
}
}
continue; }
}
if !self.errors.is_empty() {
return Err(self.errors.clone());
}
let config = match config {
Some(c) => c,
None => {
if !sub_decoders.is_empty() && instructions.is_empty() {
DecoderConfig {
name: String::new(),
width: 0,
bit_order: BitOrder::Msb0,
endian: ByteEndian::Big,
max_units: None,
span: Span::new(&self.filename, 1, 1, 0),
}
} else {
return Err(vec![Error::new(
ErrorKind::MissingDecoderBlock,
Span::new(&self.filename, 1, 1, 0),
)]);
}
}
};
Ok(DecoderDef {
config,
type_aliases,
maps,
instructions,
sub_decoders,
})
}
fn parse_decoder_block(&mut self) -> Result<DecoderConfig, Error> {
let first_line = self.lines[self.line_idx].trim();
let block_start_line = self.line_idx;
let rest = first_line.strip_prefix("decoder ").unwrap().trim();
let name = rest
.strip_suffix('{')
.map(|s| s.trim())
.unwrap_or(rest)
.to_string();
if name.is_empty() {
return Err(Error::new(
ErrorKind::ExpectedToken("decoder name".to_string()),
self.span(8, 1),
));
}
self.advance();
let mut width: Option<u32> = None;
let mut bit_order: Option<BitOrder> = None;
let mut endian: Option<ByteEndian> = None;
let mut max_units: Option<u32> = None;
while self.line_idx < self.lines.len() {
let line = self.lines[self.line_idx].trim();
if line == "}" {
self.advance();
break;
}
if line.is_empty() || line.starts_with('#') {
self.advance();
continue;
}
if let Some(val) = line.strip_prefix("width") {
let val = val
.trim()
.strip_prefix('=')
.map(|s| s.trim())
.unwrap_or(val.trim());
match val.parse::<u32>() {
Ok(w) if w == 8 || w == 16 || w == 32 => width = Some(w),
Ok(w) => {
return Err(Error::new(
ErrorKind::InvalidWidth(w),
self.span(0, line.len()),
));
}
Err(_) => {
return Err(Error::new(
ErrorKind::ExpectedToken("integer width (8, 16, or 32)".to_string()),
self.span(0, line.len()),
));
}
}
} else if let Some(val) = line.strip_prefix("bit_order") {
let val = val
.trim()
.strip_prefix('=')
.map(|s| s.trim())
.unwrap_or(val.trim());
match val {
"msb0" => bit_order = Some(BitOrder::Msb0),
"lsb0" => bit_order = Some(BitOrder::Lsb0),
_ => {
return Err(Error::new(
ErrorKind::ExpectedToken("msb0 or lsb0".to_string()),
self.span(0, line.len()),
));
}
}
} else if let Some(val) = line.strip_prefix("endian") {
let val = val
.trim()
.strip_prefix('=')
.map(|s| s.trim())
.unwrap_or(val.trim());
match val {
"big" => endian = Some(ByteEndian::Big),
"little" => endian = Some(ByteEndian::Little),
_ => {
return Err(Error::new(
ErrorKind::ExpectedToken("big or little".to_string()),
self.span(0, line.len()),
));
}
}
} else if let Some(val) = line.strip_prefix("max_units") {
let val = val
.trim()
.strip_prefix('=')
.map(|s| s.trim())
.unwrap_or(val.trim());
match val.parse::<u32>() {
Ok(m) if m > 0 => max_units = Some(m),
Ok(_) => {
return Err(Error::new(
ErrorKind::ExpectedToken("positive integer for max_units".to_string()),
self.span(0, line.len()),
));
}
Err(_) => {
return Err(Error::new(
ErrorKind::ExpectedToken("positive integer for max_units".to_string()),
self.span(0, line.len()),
));
}
}
}
self.advance();
}
let width = width.unwrap_or(32);
let bit_order = bit_order.unwrap_or(BitOrder::Msb0);
let endian = endian.unwrap_or(ByteEndian::Big);
Ok(DecoderConfig {
name,
width,
bit_order,
endian,
max_units,
span: Span::new(&self.filename, block_start_line + 1, 1, 0),
})
}
fn parse_subdecoder_block(&mut self) -> Result<SubDecoderDef, Error> {
let first_line = self.lines[self.line_idx].trim();
let block_start_line = self.line_idx;
let rest = first_line.strip_prefix("subdecoder ").unwrap().trim();
let name = rest
.strip_suffix('{')
.map(|s| s.trim())
.unwrap_or(rest)
.to_string();
if name.is_empty() {
return Err(Error::new(
ErrorKind::ExpectedToken("subdecoder name".to_string()),
self.span(12, 1),
));
}
self.advance();
let mut width: Option<u32> = None;
let mut bit_order: Option<BitOrder> = None;
while self.line_idx < self.lines.len() {
let line = self.lines[self.line_idx].trim();
if line == "}" {
self.advance();
break;
}
if line.is_empty() || line.starts_with('#') {
self.advance();
continue;
}
if let Some(val) = line.strip_prefix("width") {
let val = val
.trim()
.strip_prefix('=')
.map(|s| s.trim())
.unwrap_or(val.trim());
match val.parse::<u32>() {
Ok(w) if w == 8 || w == 16 || w == 32 => width = Some(w),
Ok(w) => {
return Err(Error::new(
ErrorKind::InvalidWidth(w),
self.span(0, line.len()),
));
}
Err(_) => {
return Err(Error::new(
ErrorKind::ExpectedToken("integer width (8, 16, or 32)".to_string()),
self.span(0, line.len()),
));
}
}
} else if let Some(val) = line.strip_prefix("bit_order") {
let val = val
.trim()
.strip_prefix('=')
.map(|s| s.trim())
.unwrap_or(val.trim());
match val {
"msb0" => bit_order = Some(BitOrder::Msb0),
"lsb0" => bit_order = Some(BitOrder::Lsb0),
_ => {
return Err(Error::new(
ErrorKind::ExpectedToken("msb0 or lsb0".to_string()),
self.span(0, line.len()),
));
}
}
}
self.advance();
}
let width = width.unwrap_or(8);
let bit_order = bit_order.unwrap_or(BitOrder::Msb0);
Ok(SubDecoderDef {
name,
width,
bit_order,
maps: Vec::new(),
instructions: Vec::new(),
span: Span::new(&self.filename, block_start_line + 1, 1, 0),
})
}
fn parse_fragment_lines(&mut self) -> Vec<FragmentLine> {
let mut fragments = Vec::new();
while self.line_idx < self.lines.len() {
let line = self.lines[self.line_idx];
let trimmed = line.trim();
if !trimmed.starts_with('|') {
break;
}
let content = trimmed[1..].trim();
let span = self.span(0, line.len());
match self.parse_single_fragment_line(content, &span) {
Ok(fl) => fragments.push(fl),
Err(e) => self.errors.push(e),
}
self.advance();
}
fragments
}
fn parse_single_fragment_line(
&self,
content: &str,
span: &Span,
) -> Result<FragmentLine, Error> {
if !content.starts_with('.') {
return Err(Error::new(
ErrorKind::InvalidFormatString(
"sub-decoder format line must start with '.name = \"...\"'".to_string(),
),
span.clone(),
));
}
let rest = &content[1..]; let eq_pos = rest.find('=').ok_or_else(|| {
Error::new(
ErrorKind::ExpectedToken("'=' in fragment line".to_string()),
span.clone(),
)
})?;
let name = rest[..eq_pos].trim().to_string();
let rhs = rest[eq_pos + 1..].trim();
let fmt_str = extract_quoted_string(rhs).ok_or_else(|| {
Error::new(
ErrorKind::InvalidFormatString(
"fragment line must contain a quoted string".to_string(),
),
span.clone(),
)
})?;
let pieces = parse_format_string(fmt_str, span)?;
Ok(FragmentLine {
name,
pieces,
span: span.clone(),
})
}
fn parse_type_alias(&self, line: &str) -> Result<TypeAlias, Error> {
let rest = line.strip_prefix("type ").unwrap().trim();
let eq_pos = rest.find('=').ok_or_else(|| {
Error::new(
ErrorKind::ExpectedToken("'=' in type alias".to_string()),
self.span(5, rest.len()),
)
})?;
let name = rest[..eq_pos].trim().to_string();
let rhs = rest[eq_pos + 1..].trim();
let (base_str, transforms, display_format) = if let Some(brace_pos) = rhs.find('{') {
let close = rhs.rfind('}').ok_or_else(|| {
Error::new(
ErrorKind::ExpectedToken("closing '}'".to_string()),
self.span(0, line.len()),
)
})?;
let transforms_str = &rhs[brace_pos + 1..close];
let (transforms, display_format) = self.parse_transforms(transforms_str)?;
(rhs[..brace_pos].trim(), transforms, display_format)
} else {
(rhs, Vec::new(), None)
};
let base_type = base_str.to_string();
Ok(TypeAlias {
name,
base_type,
transforms,
display_format,
span: self.span(0, line.len()),
})
}
fn parse_transforms(&self, s: &str) -> Result<(Vec<Transform>, Option<DisplayFormat>), Error> {
let mut transforms = Vec::new();
let mut display_format = None;
for part in s.split(',') {
let part = part.trim();
if part.is_empty() {
continue;
}
if let Some(inner) = part
.strip_prefix("sign_extend(")
.and_then(|s| s.strip_suffix(')'))
{
let n: u32 = inner.trim().parse().map_err(|_| {
Error::new(
ErrorKind::ExpectedToken("integer for sign_extend".to_string()),
self.span(0, 0),
)
})?;
transforms.push(Transform::SignExtend(n));
} else if let Some(inner) = part
.strip_prefix("zero_extend(")
.and_then(|s| s.strip_suffix(')'))
{
let n: u32 = inner.trim().parse().map_err(|_| {
Error::new(
ErrorKind::ExpectedToken("integer for zero_extend".to_string()),
self.span(0, 0),
)
})?;
transforms.push(Transform::ZeroExtend(n));
} else if let Some(inner) = part
.strip_prefix("shift_left(")
.and_then(|s| s.strip_suffix(')'))
{
let n: u32 = inner.trim().parse().map_err(|_| {
Error::new(
ErrorKind::ExpectedToken("integer for shift_left".to_string()),
self.span(0, 0),
)
})?;
transforms.push(Transform::ShiftLeft(n));
} else if let Some(inner) = part
.strip_prefix("display(")
.and_then(|s| s.strip_suffix(')'))
{
let fmt = match inner.trim() {
"signed_hex" => DisplayFormat::SignedHex,
"hex" => DisplayFormat::Hex,
other => {
return Err(Error::new(
ErrorKind::UnexpectedToken(format!(
"unknown display format: {}",
other
)),
self.span(0, part.len()),
));
}
};
display_format = Some(fmt);
} else {
return Err(Error::new(
ErrorKind::UnexpectedToken(part.to_string()),
self.span(0, part.len()),
));
}
}
Ok((transforms, display_format))
}
fn parse_instruction(&self, line: &str) -> Result<InstructionDef, Error> {
let name_end = line.find(|c: char| c.is_whitespace()).unwrap_or(line.len());
let name = line[..name_end].to_string();
let rest = line[name_end..].trim();
let segments = self.parse_segments(rest)?;
Ok(InstructionDef {
name,
segments,
format_lines: Vec::new(),
span: self.span(0, line.len()),
})
}
fn parse_segments(&self, input: &str) -> Result<Vec<Segment>, Error> {
let mut segments = Vec::new();
let mut pos = 0;
let bytes = input.as_bytes();
while pos < input.len() {
while pos < input.len() && input.as_bytes()[pos].is_ascii_whitespace() {
pos += 1;
}
if pos >= input.len() {
break;
}
if bytes[pos] == b'[' {
let seg = self.parse_fixed_segment(input, &mut pos)?;
segments.push(seg);
} else {
let seg = self.parse_field_segment(input, &mut pos)?;
segments.push(seg);
}
}
Ok(segments)
}
fn parse_fixed_segment(&self, input: &str, pos: &mut usize) -> Result<Segment, Error> {
let (range, _) = self.parse_bit_range(input, pos)?;
if *pos >= input.len() || input.as_bytes()[*pos] != b'=' {
return Err(Error::new(
ErrorKind::ExpectedToken("'=' after bit range for fixed segment".to_string()),
self.span(*pos, 1),
));
}
*pos += 1;
let start = *pos;
while *pos < input.len()
&& (input.as_bytes()[*pos] == b'0'
|| input.as_bytes()[*pos] == b'1'
|| input.as_bytes()[*pos] == b'?')
{
*pos += 1;
}
let pattern_str = &input[start..*pos];
if pattern_str.is_empty() {
return Err(Error::new(
ErrorKind::InvalidBitPattern("empty pattern".to_string()),
self.span(start, 1),
));
}
let pattern: Vec<Bit> = pattern_str
.chars()
.map(|c| match c {
'0' => Bit::Zero,
'1' => Bit::One,
'?' => Bit::Wildcard,
_ => unreachable!(),
})
.collect();
Ok(Segment::Fixed {
ranges: vec![range], pattern,
span: self.span(start, pattern_str.len()),
})
}
fn parse_field_segment(&self, input: &str, pos: &mut usize) -> Result<Segment, Error> {
let name_start = *pos;
while *pos < input.len() && input.as_bytes()[*pos] != b':' {
*pos += 1;
}
let name = input[name_start..*pos].to_string();
if *pos >= input.len() || input.as_bytes()[*pos] != b':' {
return Err(Error::new(
ErrorKind::ExpectedToken("':' after field name".to_string()),
self.span(*pos, 1),
));
}
*pos += 1;
let type_start = *pos;
while *pos < input.len() && input.as_bytes()[*pos] != b'[' && input.as_bytes()[*pos] != b'{'
{
*pos += 1;
}
let type_name = input[type_start..*pos].trim().to_string();
let transforms = if *pos < input.len() && input.as_bytes()[*pos] == b'{' {
*pos += 1; let brace_start = *pos;
while *pos < input.len() && input.as_bytes()[*pos] != b'}' {
*pos += 1;
}
let transforms_str = &input[brace_start..*pos];
if *pos < input.len() {
*pos += 1; }
Some(self.parse_transforms(transforms_str)?.0)
} else {
None
};
let (range, _) = self.parse_bit_range(input, pos)?;
let field_type = match transforms {
Some(t) => FieldType::Inline {
base_type: type_name,
transforms: t,
},
None => {
if is_builtin_type(&type_name) {
FieldType::Inline {
base_type: type_name,
transforms: Vec::new(),
}
} else {
FieldType::Alias(type_name)
}
}
};
Ok(Segment::Field {
name,
field_type,
ranges: vec![range], span: self.span(name_start, *pos - name_start),
})
}
fn parse_map_block(&mut self) -> Result<MapDef, Error> {
let first_line = self.lines[self.line_idx].trim();
let block_start_line = self.line_idx;
let rest = first_line.strip_prefix("map ").unwrap().trim();
let paren_pos = rest.find('(').ok_or_else(|| {
Error::new(
ErrorKind::ExpectedToken("'(' after map name".to_string()),
self.span(4, rest.len()),
)
})?;
let name = rest[..paren_pos].trim().to_string();
let close_paren = rest.find(')').ok_or_else(|| {
Error::new(
ErrorKind::ExpectedToken("')' in map definition".to_string()),
self.span(0, rest.len()),
)
})?;
let params_str = &rest[paren_pos + 1..close_paren];
let params: Vec<String> = params_str
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
let after_paren = rest[close_paren + 1..].trim();
if !after_paren.starts_with('{') {
return Err(Error::new(
ErrorKind::ExpectedToken("'{' after map parameters".to_string()),
self.span(0, first_line.len()),
));
}
self.advance();
let mut entries = Vec::new();
while self.line_idx < self.lines.len() {
let line = self.lines[self.line_idx].trim();
if line == "}" {
self.advance();
break;
}
if line.is_empty() || line.starts_with('#') {
self.advance();
continue;
}
let arrow_pos = line.find("=>").ok_or_else(|| {
Error::new(
ErrorKind::ExpectedToken("'=>' in map entry".to_string()),
self.span(0, line.len()),
)
})?;
let keys_str = &line[..arrow_pos];
let output_raw = line[arrow_pos + 2..].trim();
let output_str = extract_quoted_string(output_raw).unwrap_or(output_raw);
let keys: Vec<MapKey> = keys_str
.split(',')
.map(|s| {
let s = s.trim();
if s == "_" {
MapKey::Wildcard
} else if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X"))
{
MapKey::Value(i64::from_str_radix(hex, 16).unwrap_or(0))
} else {
MapKey::Value(s.parse::<i64>().unwrap_or(0))
}
})
.collect();
let entry_span = self.span(0, line.len());
let output = parse_format_string(output_str, &entry_span)?;
entries.push(MapEntry {
keys,
output,
span: entry_span,
});
self.advance();
}
Ok(MapDef {
name,
params,
entries,
span: Span::new(&self.filename, block_start_line + 1, 1, 0),
})
}
fn parse_format_lines(&mut self) -> Vec<FormatLine> {
let mut format_lines = Vec::new();
while self.line_idx < self.lines.len() {
let line = self.lines[self.line_idx];
let trimmed = line.trim();
if !trimmed.starts_with('|') {
break;
}
let content = trimmed[1..].trim();
let span = self.span(0, line.len());
match self.parse_single_format_line(content, &span) {
Ok(fl) => format_lines.push(fl),
Err(e) => self.errors.push(e),
}
self.advance();
}
format_lines
}
fn parse_single_format_line(&self, content: &str, span: &Span) -> Result<FormatLine, Error> {
if let Some(quote_pos) = content.find('"') {
let before_quote = &content[..quote_pos];
let after_quote = &content[quote_pos..];
let fmt_str = extract_quoted_string(after_quote).ok_or_else(|| {
Error::new(
ErrorKind::InvalidFormatString("unclosed quote in format line".to_string()),
span.clone(),
)
})?;
let guard = if before_quote.trim().is_empty() {
None
} else {
let guard_str = before_quote.trim().trim_end_matches(':').trim();
Some(parse_guard(guard_str, span)?)
};
let pieces = parse_format_string(fmt_str, span)?;
Ok(FormatLine {
guard,
pieces,
span: span.clone(),
})
} else {
Err(Error::new(
ErrorKind::InvalidFormatString(
"format line must contain a quoted string".to_string(),
),
span.clone(),
))
}
}
fn parse_bit_range(
&self,
input: &str,
pos: &mut usize,
) -> Result<(BitRange, (u32, u32)), Error> {
if *pos >= input.len() || input.as_bytes()[*pos] != b'[' {
return Err(Error::new(
ErrorKind::ExpectedToken("'[' for bit range".to_string()),
self.span(*pos, 1),
));
}
*pos += 1;
let num1_start = *pos;
while *pos < input.len() && input.as_bytes()[*pos] != b':' && input.as_bytes()[*pos] != b']'
{
*pos += 1;
}
let num1: u32 = input[num1_start..*pos].trim().parse().map_err(|_| {
Error::new(
ErrorKind::InvalidRange,
self.span(num1_start, *pos - num1_start),
)
})?;
let (dsl_start, dsl_end) = if *pos < input.len() && input.as_bytes()[*pos] == b':' {
*pos += 1;
let num2_start = *pos;
while *pos < input.len() && input.as_bytes()[*pos] != b']' {
*pos += 1;
}
let num2: u32 = input[num2_start..*pos].trim().parse().map_err(|_| {
Error::new(
ErrorKind::InvalidRange,
self.span(num2_start, *pos - num2_start),
)
})?;
(num1, num2)
} else {
(num1, num1)
};
if *pos < input.len() && input.as_bytes()[*pos] == b']' {
*pos += 1;
} else {
return Err(Error::new(
ErrorKind::ExpectedToken("']'".to_string()),
self.span(*pos, 1),
));
}
Ok((BitRange::new(dsl_start, dsl_end), (dsl_start, dsl_end)))
}
}
fn extract_quoted_string(s: &str) -> Option<&str> {
let s = s.trim();
if s.starts_with('"') {
let inner = &s[1..];
let mut i = 0;
let chars: Vec<char> = inner.chars().collect();
while i < chars.len() {
if chars[i] == '\\' {
i += 2;
} else if chars[i] == '"' {
let byte_pos: usize = inner[..]
.char_indices()
.nth(i)
.map(|(p, _)| p)
.unwrap_or(inner.len());
return Some(&inner[..byte_pos]);
} else {
i += 1;
}
}
}
None
}
fn parse_guard(s: &str, span: &Span) -> Result<Guard, Error> {
let mut conditions = Vec::new();
let parts: Vec<&str> = if s.contains("&&") {
s.split("&&").collect()
} else {
s.split(',').collect()
};
for part in parts {
let part = part.trim();
if part.is_empty() {
continue;
}
let cond = parse_guard_condition(part, span)?;
conditions.push(cond);
}
if conditions.is_empty() {
return Err(Error::new(
ErrorKind::InvalidGuard("empty guard".to_string()),
span.clone(),
));
}
Ok(Guard { conditions })
}
fn parse_guard_condition(s: &str, span: &Span) -> Result<GuardCondition, Error> {
let ops: &[(&str, CompareOp)] = &[
("!=", CompareOp::Ne),
("<=", CompareOp::Le),
(">=", CompareOp::Ge),
("==", CompareOp::Eq),
("<", CompareOp::Lt),
(">", CompareOp::Gt),
];
for &(op_str, ref op) in ops {
if let Some(pos) = s.find(op_str) {
let left = s[..pos].trim();
let right = s[pos + op_str.len()..].trim();
return Ok(GuardCondition {
left: parse_guard_operand(left, span)?,
op: op.clone(),
right: parse_guard_operand(right, span)?,
});
}
}
Err(Error::new(
ErrorKind::InvalidGuard(format!("no operator found in '{}'", s)),
span.clone(),
))
}
fn parse_guard_operand(s: &str, span: &Span) -> Result<GuardOperand, Error> {
let s = s.trim();
if s.is_empty() {
return Err(Error::new(
ErrorKind::InvalidGuard("empty operand".to_string()),
span.clone(),
));
}
if let Some(op_pos) = find_guard_arith_op(s, &['+', '-']) {
let left = s[..op_pos].trim();
let op_char = s.as_bytes()[op_pos] as char;
let right = s[op_pos + 1..].trim();
let op = match op_char {
'+' => ArithOp::Add,
'-' => ArithOp::Sub,
_ => unreachable!(),
};
return Ok(GuardOperand::Expr {
left: Box::new(parse_guard_operand(left, span)?),
op,
right: Box::new(parse_guard_operand(right, span)?),
});
}
if let Some(op_pos) = find_guard_arith_op(s, &['*', '/', '%']) {
let left = s[..op_pos].trim();
let op_char = s.as_bytes()[op_pos] as char;
let right = s[op_pos + 1..].trim();
let op = match op_char {
'*' => ArithOp::Mul,
'/' => ArithOp::Div,
'%' => ArithOp::Mod,
_ => unreachable!(),
};
return Ok(GuardOperand::Expr {
left: Box::new(parse_guard_operand(left, span)?),
op,
right: Box::new(parse_guard_operand(right, span)?),
});
}
if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
if let Ok(val) = i64::from_str_radix(hex, 16) {
return Ok(GuardOperand::Literal(val));
}
}
if let Ok(val) = s.parse::<i64>() {
return Ok(GuardOperand::Literal(val));
}
Ok(GuardOperand::Field(s.to_string()))
}
fn find_guard_arith_op(s: &str, ops: &[char]) -> Option<usize> {
let bytes = s.as_bytes();
let mut last = None;
for (i, &b) in bytes.iter().enumerate() {
if i > 0 && ops.contains(&(b as char)) {
last = Some(i);
}
}
last
}
fn is_builtin_type(name: &str) -> bool {
matches!(
name,
"u1" | "u2"
| "u3"
| "u4"
| "u5"
| "u6"
| "u7"
| "u8"
| "u16"
| "u32"
| "i8"
| "i16"
| "i32"
| "bool"
)
}
pub fn dsl_to_hardware(dsl_start: u32, dsl_end: u32, width: u32, order: BitOrder) -> Vec<BitRange> {
let (dsl_lo, dsl_hi) = (dsl_start.min(dsl_end), dsl_start.max(dsl_end));
let start_unit = dsl_lo / width;
let end_unit = dsl_hi / width;
let mut ranges = Vec::new();
for unit in start_unit..=end_unit {
let unit_dsl_start = unit * width;
let unit_dsl_end = (unit + 1) * width - 1;
let range_start_in_unit = if unit == start_unit {
dsl_lo
} else {
unit_dsl_start
};
let range_end_in_unit = if unit == end_unit {
dsl_hi
} else {
unit_dsl_end
};
let local_start = range_start_in_unit % width;
let local_end = range_end_in_unit % width;
let (hw_start, hw_end) = match order {
BitOrder::Msb0 => {
let hw_a = width - 1 - local_start;
let hw_b = width - 1 - local_end;
(std::cmp::max(hw_a, hw_b), std::cmp::min(hw_a, hw_b))
}
BitOrder::Lsb0 => (
std::cmp::max(local_start, local_end),
std::cmp::min(local_start, local_end),
),
};
ranges.push(BitRange::new_in_unit(unit, hw_start, hw_end));
}
ranges
}