use crate::cat::*;
use moore_common::errors::{DiagBuilder2, DiagResult2};
use moore_common::source::*;
use std::{collections::HashMap, fmt, path::Path, rc::Rc};
use once_cell::sync::Lazy;
type TokenAndSpan = (CatTokenKind, Span);
pub struct Preprocessor<'a> {
stack: Vec<Stream<'a>>,
contents: Vec<Rc<dyn SourceContent>>,
token: Option<TokenAndSpan>,
macro_defs: HashMap<String, Macro>,
macro_stack: Vec<TokenAndSpan>,
include_paths: &'a [&'a Path],
defcond_stack: Vec<Defcond>,
dirs: Directives,
}
impl<'a> Preprocessor<'a> {
pub fn new(
source: Source,
include_paths: &'a [&'a Path],
macro_defs: &'a [(&'a str, Option<&'a str>)],
) -> Preprocessor<'a> {
let content = source.get_content();
let content_unbound = unsafe { &*(content.as_ref() as *const dyn SourceContent) };
let iter = content_unbound.iter();
let macro_defs = macro_defs
.into_iter()
.map(|(name, value)| {
let body = match value {
Some(value) => {
let src = get_source_manager().add_anonymous(*value);
let span = Span::new(src, 0, value.len());
Cat::new(Box::new(value.char_indices()))
.map(|x| (x.0, span))
.collect()
}
None => Vec::new(),
};
(
name.to_string(),
Macro {
name: name.to_string(),
span: INVALID_SPAN,
args: Vec::new(),
body: body,
},
)
})
.collect();
Preprocessor {
stack: vec![Stream {
source: source,
iter: Cat::new(iter),
}],
contents: vec![content],
token: None,
macro_defs,
macro_stack: Vec::new(),
include_paths: include_paths,
defcond_stack: Vec::new(),
dirs: Default::default(),
}
}
fn bump(&mut self) {
self.token = self.macro_stack.pop();
if self.token.is_some() {
return;
}
loop {
self.token = match self.stack.last_mut() {
Some(stream) => stream
.iter
.next()
.map(|tkn| (tkn.0, Span::new(stream.source, tkn.1, tkn.2))),
None => return,
};
if self.token.is_none() {
self.stack.pop();
} else {
break;
}
}
}
fn handle_directive<S: AsRef<str>>(&mut self, dir_name: S, span: Span) -> DiagResult2<()> {
let dir_name = dir_name.as_ref();
let dir = DIRECTIVES_TABLE
.get(dir_name)
.map(|x| *x)
.unwrap_or(Directive::Unknown);
match dir {
Directive::Include => {
if self.is_inactive() {
return Ok(());
}
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
let name_p;
let name_q;
let closing =
match self.token {
Some((Symbol('"'), sp)) => {
name_p = sp.end();
self.bump();
'"'
}
Some((Symbol('<'), sp)) => {
name_p = sp.end();
self.bump();
'>'
}
_ => return Err(DiagBuilder2::fatal(
"expected filename inside double quotes (\"...\") or angular brackets \
(<...>) after `include",
)
.span(span)),
};
let mut filename = String::new();
loop {
match self.token {
Some((Symbol(c), sp)) if c == closing => {
name_q = sp.begin();
break;
}
Some((Newline, sp)) => {
return Err(DiagBuilder2::fatal(
"expected end of included file's name before line break",
)
.span(sp));
}
Some((_, sp)) => {
filename.push_str(&sp.extract());
self.bump();
}
None => {
return Err(DiagBuilder2::fatal(
"expected filename after `include directive before the end of the \
input",
)
.span(span));
}
}
}
let included_source = match self.open_include(&filename, &span.source.get_path()) {
Some(src) => src,
None => {
return Err(DiagBuilder2::fatal(format!(
"cannot open included file \"{}\"",
filename
))
.span(Span::union(name_p, name_q)));
}
};
let content = included_source.get_content();
let content_unbound = unsafe { &*(content.as_ref() as *const dyn SourceContent) };
let iter = content_unbound.iter();
self.contents.push(content);
self.stack.push(Stream {
source: included_source,
iter: Cat::new(iter),
});
self.bump();
return Ok(());
}
Directive::Define => {
if self.is_inactive() {
return Ok(());
}
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
let makro = self.handle_macro_definition(span)?;
self.macro_defs.insert(makro.name.clone(), makro);
return Ok(());
}
Directive::Undef => {
if self.is_inactive() {
return Ok(());
}
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
let (name, _) = match self.try_eat_name() {
Some(x) => x,
None => {
return Err(
DiagBuilder2::fatal("expected macro name after \"`undef\"").span(span)
);
}
};
self.macro_defs.remove(&name);
return Ok(());
}
Directive::Undefineall => {
if self.is_inactive() {
return Ok(());
}
self.macro_defs.clear();
}
Directive::Ifdef | Directive::Ifndef | Directive::Elsif => {
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
let name = match self.try_eat_name() {
Some((x, _)) => x,
_ => {
return Err(DiagBuilder2::fatal(format!(
"expected macro name after {}",
dir_name
))
.span(span));
}
};
let exists = self.macro_defs.contains_key(&name);
match dir {
Directive::Ifdef => self.defcond_stack.push(if self.is_inactive() {
Defcond::Done
} else if exists {
Defcond::Enabled
} else {
Defcond::Disabled
}),
Directive::Ifndef => self.defcond_stack.push(if self.is_inactive() {
Defcond::Done
} else if exists {
Defcond::Disabled
} else {
Defcond::Enabled
}),
Directive::Elsif => {
match self.defcond_stack.pop() {
Some(Defcond::Done) | Some(Defcond::Enabled) => {
self.defcond_stack.push(Defcond::Done)
}
Some(Defcond::Disabled) => {
self.defcond_stack.push(if self.is_inactive() {
Defcond::Done
} else if exists {
Defcond::Enabled
} else {
Defcond::Disabled
})
}
None => {
return Err(DiagBuilder2::fatal(
"found `elsif without any preceeding `ifdef, `ifndef, or \
`elsif directive",
)
.span(span))
}
};
}
_ => unreachable!(),
}
return Ok(());
}
Directive::Else => {
match self.defcond_stack.pop() {
Some(Defcond::Disabled) => self.defcond_stack.push(Defcond::Enabled),
Some(Defcond::Enabled) | Some(Defcond::Done) => {
self.defcond_stack.push(Defcond::Done)
}
None => {
return Err(DiagBuilder2::fatal(
"found `else without any preceeding `ifdef, `ifndef, or `elsif \
directive",
)
.span(span))
}
}
return Ok(());
}
Directive::Endif => {
if self.defcond_stack.pop().is_none() {
return Err(DiagBuilder2::fatal(
"found `endif without any preceeding `ifdef, `ifndef, `else, or `elsif \
directive",
)
.span(span));
}
return Ok(());
}
Directive::Unknown => {
if self.is_inactive() {
return Ok(());
}
if let Some(ref makro) = unsafe { &*(self as *const Preprocessor) }
.macro_defs
.get(dir_name)
{
let args = self.handle_macro_expansion_args(makro, span)?;
match self.token {
Some((x, sp)) => self.macro_stack.push((x, sp)),
None => (),
}
if args.is_empty() {
self.macro_stack
.extend(makro.body.iter().rev().map(|&(tkn, sp)| (tkn, sp)));
} else {
let mut replacement = Vec::<TokenAndSpan>::new();
for tkn in &makro.body {
match *tkn {
(Text, sp) => match args.get(&sp.extract()) {
Some(substitute) => {
replacement.extend(substitute);
}
None => replacement.push(*tkn),
},
x => replacement.push(x),
}
}
self.macro_stack
.extend(replacement.iter().rev().map(|&(tkn, sp)| (tkn, sp)));
}
self.bump();
return Ok(());
}
}
Directive::Timescale => {
while let Some((tkn, _)) = self.token {
if tkn == Newline {
break;
}
self.bump();
}
return Ok(());
}
Directive::CurrentFile => {
if !self.is_inactive() {
self.macro_stack.push((CatTokenKind::Text, span));
}
return Ok(());
}
Directive::CurrentLine => {
if !self.is_inactive() {
self.macro_stack.push((CatTokenKind::Digits, span));
}
return Ok(());
}
Directive::Resetall => {
if !self.is_inactive() {
self.dirs = Default::default();
}
return Ok(());
}
Directive::Celldefine => {
if !self.is_inactive() {
self.dirs.celldefine = true;
}
return Ok(());
}
Directive::Endcelldefine => {
if !self.is_inactive() {
self.dirs.celldefine = false;
}
return Ok(());
}
Directive::DefaultNettype => {
if !self.is_inactive() {
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
let tkn = match self.token {
Some(tkn @ (Text, _)) => {
self.bump();
tkn
}
_ => {
return Err(DiagBuilder2::fatal(
"expected nettype after `default_nettype",
)
.span(span));
}
};
self.dirs.default_nettype = if tkn.1.extract() == "none" {
None
} else {
Some(tkn)
};
debug!(
"Set default_nettype to `{}`",
self.dirs
.default_nettype
.map(|(_, sp)| sp.extract())
.unwrap_or_else(|| "none".to_string())
);
}
return Ok(());
}
Directive::BeginKeywords => {
if !self.is_inactive() {
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
match self.token {
Some((Symbol('"'), _)) => self.bump(),
_ => {
return Err(DiagBuilder2::fatal("expected `\"` after `begin_keywords")
.span(span));
}
};
let mut spec = String::new();
while let Some(tkn) = self.token {
if tkn.0 == Symbol('"') {
break;
}
spec.push_str(&tkn.1.extract());
self.bump();
}
match self.token {
Some((Symbol('"'), _)) => self.bump(),
_ => {
return Err(DiagBuilder2::fatal(
"expected `\"` after version specifier",
)
.span(span));
}
};
let spec = match KeywordsDirective::from_str(&spec) {
Some(spec) => spec,
_ => {
return Err(DiagBuilder2::fatal(format!(
"unknown `begin_keywords version specifier `{}`",
spec
))
.span(span));
}
};
self.dirs.keywords.push(spec);
debug!("Push keywords; now `{:?}`", self.dirs.keywords.last());
}
return Ok(());
}
Directive::EndKeywords => {
if !self.is_inactive() {
if self.dirs.keywords.pop().is_none() {
return Err(DiagBuilder2::fatal(
"`end_keywords without earlier `begin_keywords",
)
.span(span));
}
debug!("Pop keywords; now `{:?}`", self.dirs.keywords.last());
}
return Ok(());
}
Directive::Line => {
if !self.is_inactive() {
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
let _line = match self.token {
Some((Digits, sp)) => {
self.bump();
sp
}
_ => {
return Err(
DiagBuilder2::fatal("expected line number after `line").span(span)
);
}
};
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
match self.token {
Some((Symbol('"'), _)) => self.bump(),
_ => {
return Err(DiagBuilder2::fatal(
"expected `\"` after line number in `line",
)
.span(span));
}
};
let mut filename = String::new();
while let Some(tkn) = self.token {
if tkn.0 == Symbol('"') {
break;
}
filename.push_str(&tkn.1.extract());
self.bump();
}
match self.token {
Some((Symbol('"'), _)) => self.bump(),
_ => {
return Err(DiagBuilder2::fatal(
"expected `\"` after filename in `line",
)
.span(span));
}
};
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
let _level = match self.token {
Some((Digits, sp)) => {
self.bump();
sp
}
_ => {
return Err(
DiagBuilder2::fatal("expected level after `line").span(span)
);
}
};
debug!("Ignoring `line directive");
}
return Ok(());
}
Directive::UnconnectedDrive => {
if !self.is_inactive() {
match self.token {
Some((Whitespace, _)) => self.bump(),
_ => (),
}
let tkn = match self.token {
Some((Text, _)) => self.token,
_ => None,
};
let pull = match tkn.map(|(_, sp)| sp.extract()).as_ref().map(|s| s.as_str()) {
Some("pull0") => UnconnectedDrive::Pull0,
Some("pull1") => UnconnectedDrive::Pull1,
_ => {
return Err(DiagBuilder2::fatal(
"expected `pull0` or `pull1` after `unconnected_drive",
)
.span(span));
}
};
self.bump();
self.dirs.unconnected_drive = Some(pull);
debug!("Set unconnected_drive to {:?}", self.dirs.unconnected_drive);
}
return Ok(());
}
Directive::NoUnconnectedDrive => {
if !self.is_inactive() {
self.dirs.unconnected_drive = None;
debug!("Set unconnected_drive to {:?}", self.dirs.unconnected_drive);
}
return Ok(());
}
}
return Err(
DiagBuilder2::fatal(format!("unknown compiler directive '`{}'", dir_name)).span(span),
);
}
fn open_include(&mut self, filename: &str, current_file: &str) -> Option<Source> {
let first = [Path::new(current_file)
.parent()
.expect("current file path must have a valid parent")];
let prefices = first.iter().chain(self.include_paths.iter());
let sm = get_source_manager();
for prefix in prefices {
let mut buf = prefix.to_path_buf();
buf.push(filename);
let src = sm.open(buf.to_str().unwrap());
if src.is_some() {
return src;
}
}
return None;
}
fn is_inactive(&self) -> bool {
match self.defcond_stack.last() {
Some(&Defcond::Enabled) | None => false,
_ => true,
}
}
fn try_eat_name(&mut self) -> Option<(String, Span)> {
let (mut name, mut span) = match self.token {
Some((Text, sp)) | Some((Symbol('_'), sp)) => (sp.extract(), sp),
_ => return None,
};
self.bump();
loop {
match self.token {
Some((Text, sp)) | Some((Digits, sp)) | Some((Symbol('_'), sp)) => {
name.push_str(&sp.extract());
span.expand(sp);
self.bump();
}
_ => break,
}
}
Some((name, span))
}
fn skip_whitespace(&mut self) -> bool {
match self.token {
Some((Whitespace, _)) => {
self.bump();
true
}
_ => false,
}
}
fn handle_macro_definition(&mut self, define_span: Span) -> Result<Macro, DiagBuilder2> {
let mut all_span = define_span;
let (name, name_span) = match self.try_eat_name() {
Some(x) => x,
None => {
return Err(
DiagBuilder2::fatal("expected macro name after \"`define\"").span(define_span)
);
}
};
all_span.expand(define_span);
let mut makro = Macro::new(name, name_span);
makro.args = self.handle_macro_definition_args()?;
self.skip_whitespace();
loop {
match self.token {
Some((Newline, _)) => {
self.bump();
break;
}
Some((Symbol('\\'), _)) => {
self.bump();
match self.token {
Some((Newline, _)) => self.bump(),
_ => (),
};
}
Some(x) => {
makro.body.push(x);
self.bump();
}
None => break,
}
}
Ok(makro)
}
fn handle_macro_definition_args(&mut self) -> Result<Vec<MacroArg>, DiagBuilder2> {
let mut all_span = match self.token {
Some((Symbol('('), sp)) => {
self.bump();
sp
}
_ => return Ok(vec![]),
};
let mut args = vec![];
loop {
self.skip_whitespace();
match self.token {
Some((Symbol(')'), _)) => {
self.bump();
break;
}
None => {
return Err(
DiagBuilder2::fatal("expected `)` after macro arguments").span(all_span)
)
}
_ => (),
}
let (name, name_span) = match self.try_eat_name() {
Some(x) => x,
_ => {
return Err(
DiagBuilder2::fatal("expected macro argument name").span(all_span.end())
);
}
};
self.skip_whitespace();
let default = match self.token {
Some((Symbol('='), sp)) => {
all_span.expand(sp);
self.bump();
self.skip_whitespace();
let mut tokens = vec![];
let mut nesting = 0;
loop {
match self.token {
Some((Symbol(','), _)) | Some((Symbol(')'), _)) if nesting == 0 => {
match tokens.last() {
Some((Whitespace, _)) => {
tokens.pop();
}
_ => (),
}
break;
}
Some(x @ (Symbol('('), _))
| Some(x @ (Symbol('{'), _))
| Some(x @ (Symbol('['), _)) => {
nesting += 1;
all_span.expand(x.1);
tokens.push(x);
self.bump();
}
Some(x @ (Symbol(')'), _))
| Some(x @ (Symbol('}'), _))
| Some(x @ (Symbol(']'), _)) => {
nesting -= 1;
all_span.expand(x.1);
tokens.push(x);
self.bump();
}
Some(x) => {
all_span.expand(x.1);
tokens.push(x);
self.bump();
}
_ => break,
}
}
Some(tokens)
}
_ => None,
};
args.push(MacroArg {
name,
span: name_span,
default,
});
self.skip_whitespace();
match self.token {
Some((Symbol(','), _)) => self.bump(),
Some((Symbol(')'), _)) => (),
Some((_, sp)) => {
return Err(
DiagBuilder2::fatal("expected `,` or `)` after macro argument").span(sp),
)
}
None => (),
}
}
Ok(args)
}
fn handle_macro_expansion_args(
&mut self,
makro: &Macro,
span: Span,
) -> Result<MacroExpansionParams, DiagBuilder2> {
if makro.args.is_empty() {
return Ok(Default::default());
}
let mut all_span = span;
self.skip_whitespace();
match self.token {
Some((Symbol('('), sp)) => {
self.bump();
all_span.expand(sp);
}
_ => {
return Err(DiagBuilder2::fatal(format!(
"expected macro arguments for `{}`",
makro.name
))
.span(all_span)
.add_note(format!(
"At least parenthesis are needed: `{}()`",
makro.name
))
.add_note(format!(
"Macro `{}` defines {} arguments:",
makro.name,
makro.args.len()
))
.span(makro.span));
}
}
self.skip_whitespace();
let mut args = vec![];
'outer: loop {
let mut arg_tokens = Vec::<TokenAndSpan>::new();
let mut nesting = 0;
loop {
match self.token {
Some((Symbol(','), sp)) if nesting == 0 => {
args.push(arg_tokens);
all_span.expand(sp);
self.bump();
self.skip_whitespace();
break;
}
Some((Symbol(')'), sp)) if nesting == 0 => {
args.push(arg_tokens);
all_span.expand(sp);
self.bump();
break 'outer;
}
Some(x @ (Symbol('('), _))
| Some(x @ (Symbol('{'), _))
| Some(x @ (Symbol('['), _)) => {
arg_tokens.push(x);
nesting += 1;
self.bump();
all_span.expand(x.1);
}
Some(x @ (Symbol(')'), _))
| Some(x @ (Symbol('}'), _))
| Some(x @ (Symbol(']'), _)) => {
arg_tokens.push(x);
nesting -= 1;
self.bump();
all_span.expand(x.1);
}
Some(x) => {
arg_tokens.push(x);
self.bump();
all_span.expand(x.1);
}
None => {
return Err(DiagBuilder2::fatal("expected `)` after macro arguments")
.span(all_span));
}
}
}
}
for arg in &mut args {
match arg.last() {
Some((Whitespace, _)) => {
arg.pop();
}
_ => (),
}
}
if makro.args.len() < args.len() {
let d = DiagBuilder2::fatal(format!(
"macro expansion with {} arguments, but `{}` expects {} arguments",
args.len(),
makro.name,
makro.args.len()
))
.span(all_span)
.add_note(format!("Definition of `{}` was here:", makro.name))
.span(makro.span);
return Err(d);
}
let args = makro
.args
.iter()
.zip(args.into_iter().map(Some).chain(std::iter::repeat(None)))
.map(|(def, exp)| {
let is_empty = !exp.iter().flatten().any(|(t, _)| match t {
Whitespace => false,
_ => true,
});
let value = match (exp, def.default.as_ref()) {
(Some(ref _exp), Some(default)) if is_empty => default.clone(),
(Some(ref _exp), None) if is_empty => vec![],
(Some(exp), _) => exp,
(None, Some(default)) => default.clone(),
(None, None) => {
let d = DiagBuilder2::fatal(format!(
"macro expansion missing value for `{}`",
def.name
))
.span(all_span)
.add_note(format!(
"Macro argument `{}` needs a value because it has no default:",
def.name
))
.span(def.span);
return Err(d);
}
};
Ok((def.name.clone(), value))
})
.collect::<Result<HashMap<_, _>, _>>()?;
Ok(args)
}
}
type MacroExpansionParams = HashMap<String, Vec<TokenAndSpan>>;
impl<'a> Iterator for Preprocessor<'a> {
type Item = DiagResult2<TokenAndSpan>;
fn next(&mut self) -> Option<DiagResult2<TokenAndSpan>> {
if self.token.is_none() {
self.bump();
}
loop {
match self.token {
Some((Symbol('`'), sp_backtick)) => {
self.bump();
if let Some((name, sp)) = self.try_eat_name() {
let dir_span = Span::union(sp_backtick, sp);
match self.handle_directive(name, dir_span) {
Err(x) => return Some(Err(x)),
_ => (),
}
continue;
} else if let Some(tkn @ (Symbol('"'), _)) = self.token {
self.bump();
if !self.is_inactive() {
return Some(Ok(tkn));
}
} else if let Some(tkn @ (Symbol('\\'), _)) = self.token {
self.bump();
if !self.is_inactive() {
return Some(Ok(tkn));
}
} else if let Some((Symbol('`'), _)) = self.token {
self.bump();
} else {
return Some(Err(DiagBuilder2::fatal(
"expected compiler directive after '`', or '``', '`\"', or '`\\'",
)
.span(sp_backtick)));
}
}
_ => {
if self.is_inactive() {
self.bump();
} else {
let tkn = self.token.map(|x| Ok(x));
self.bump();
return tkn;
}
}
}
}
}
}
struct Stream<'a> {
source: Source,
iter: Cat<'a>,
}
#[derive(Debug, Clone, Copy)]
enum Directive {
Include,
Define,
Undef,
Undefineall,
Ifdef,
Ifndef,
Else,
Elsif,
Endif,
Timescale,
CurrentFile,
CurrentLine,
Resetall,
Celldefine,
Endcelldefine,
DefaultNettype,
BeginKeywords,
EndKeywords,
Line,
UnconnectedDrive,
NoUnconnectedDrive,
Unknown,
}
impl fmt::Display for Directive {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Directive::Include => write!(f, "`include"),
Directive::Define => write!(f, "`define"),
Directive::Undef => write!(f, "`undef"),
Directive::Undefineall => write!(f, "`undefineall"),
Directive::Ifdef => write!(f, "`ifdef"),
Directive::Ifndef => write!(f, "`ifndef"),
Directive::Else => write!(f, "`else"),
Directive::Elsif => write!(f, "`elsif"),
Directive::Endif => write!(f, "`endif"),
Directive::Timescale => write!(f, "`timescale"),
Directive::CurrentFile => write!(f, "`__FILE__"),
Directive::CurrentLine => write!(f, "`__LINE__"),
Directive::Resetall => write!(f, "`resetall"),
Directive::Celldefine => write!(f, "`celldefine"),
Directive::Endcelldefine => write!(f, "`endcelldefine"),
Directive::DefaultNettype => write!(f, "`default_nettype"),
Directive::BeginKeywords => write!(f, "`begin_keywords"),
Directive::EndKeywords => write!(f, "`end_keywords"),
Directive::Line => write!(f, "`line"),
Directive::UnconnectedDrive => write!(f, "`unconnected_drive"),
Directive::NoUnconnectedDrive => write!(f, "`nounconnected_drive"),
Directive::Unknown => write!(f, "unknown"),
}
}
}
static DIRECTIVES_TABLE: Lazy<HashMap<&'static str, Directive>> = Lazy::new(|| {
let mut table = HashMap::new();
table.insert("include", Directive::Include);
table.insert("define", Directive::Define);
table.insert("undef", Directive::Undef);
table.insert("undefineall", Directive::Undefineall);
table.insert("ifdef", Directive::Ifdef);
table.insert("ifndef", Directive::Ifndef);
table.insert("else", Directive::Else);
table.insert("elsif", Directive::Elsif);
table.insert("endif", Directive::Endif);
table.insert("__FILE__", Directive::CurrentFile);
table.insert("__LINE__", Directive::CurrentLine);
table.insert("resetall", Directive::Resetall);
table.insert("celldefine", Directive::Celldefine);
table.insert("endcelldefine", Directive::Endcelldefine);
table.insert("default_nettype", Directive::DefaultNettype);
table.insert("begin_keywords", Directive::BeginKeywords);
table.insert("end_keywords", Directive::EndKeywords);
table.insert("line", Directive::Line);
table.insert("unconnected_drive", Directive::UnconnectedDrive);
table.insert("nounconnected_drive", Directive::NoUnconnectedDrive);
table.insert("timescale", Directive::Timescale);
table
});
#[derive(Debug)]
struct Macro {
name: String,
span: Span,
args: Vec<MacroArg>,
body: Vec<TokenAndSpan>,
}
impl Macro {
fn new(name: String, span: Span) -> Macro {
Macro {
name: name,
span: span,
args: Vec::new(),
body: Vec::new(),
}
}
}
#[derive(Debug)]
struct MacroArg {
name: String,
span: Span,
default: Option<Vec<TokenAndSpan>>,
}
enum Defcond {
Done,
Enabled,
Disabled,
}
#[derive(Default)]
struct Directives {
celldefine: bool,
default_nettype: Option<TokenAndSpan>,
keywords: Vec<KeywordsDirective>,
unconnected_drive: Option<UnconnectedDrive>,
}
#[allow(non_camel_case_types)]
#[derive(Debug)]
enum KeywordsDirective {
Ieee1800_2009,
Ieee1800_2005,
Ieee1364_2005,
Ieee1364_2001,
Ieee1364_2001_Noconfig,
Ieee1364_1995,
}
#[derive(Debug)]
enum UnconnectedDrive {
Pull0,
Pull1,
}
impl KeywordsDirective {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"1800-2009" => Some(Self::Ieee1800_2009),
"1800-2005" => Some(Self::Ieee1800_2005),
"1364-2005" => Some(Self::Ieee1364_2005),
"1364-2001" => Some(Self::Ieee1364_2001),
"1364-2001-noconfig" => Some(Self::Ieee1364_2001_Noconfig),
"1364-1995" => Some(Self::Ieee1364_1995),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn preproc(input: &str) -> Preprocessor {
use std::cell::Cell;
thread_local!(static INDEX: Cell<usize> = Cell::new(0));
let sm = get_source_manager();
let idx = INDEX.with(|i| {
let v = i.get();
i.set(v + 1);
v
});
let source = sm.add(&format!("test_{}.sv", idx), input);
Preprocessor::new(source, &[], &[])
}
fn check_str(input: &str, expected: &str) {
let pp = preproc(input);
let actual: String = pp.map(|x| x.unwrap().1.extract()).collect();
assert_eq!(actual, expected);
}
#[test]
fn include() {
let sm = get_source_manager();
sm.add("other.sv", "bar\n");
sm.add("test.sv", "foo\n`include \"other.sv\"\nbaz");
let pp = Preprocessor::new(sm.open("test.sv").unwrap(), &[], &[]);
let actual: Vec<_> = pp.map(|x| x.unwrap().0).collect();
assert_eq!(actual, &[Text, Newline, Text, Newline, Newline, Text,]);
}
#[test]
fn include_and_define() {
let sm = get_source_manager();
sm.add("other.sv", "/* World */\n`define foo 42\nbar");
sm.add(
"test.sv",
"// Hello\n`include \"other.sv\"\n`foo something\n",
);
let pp = Preprocessor::new(sm.open("test.sv").unwrap(), &[], &[]);
let actual: String = pp
.map(|x| {
let x = x.unwrap();
println!("{:?}", x);
x.1.extract()
})
.collect();
assert_eq!(actual, "// Hello\n/* World */\nbar\n42 something\n");
}
#[test]
#[should_panic(expected = "unknown compiler directive")]
fn conditional_define() {
let sm = get_source_manager();
let source = sm.add("test.sv", "`ifdef FOO\n`define BAR\n`endif\n`BAR");
let mut pp = Preprocessor::new(source, &[], &[]);
while let Some(tkn) = pp.next() {
tkn.unwrap();
}
}
#[test]
fn macro_args() {
check_str(
"`define foo(x,y) {x + y _bar}\n`foo(12, foo)\n",
"{12 + foo _bar}\n",
);
}
#[test]
fn macro_noargs_parentheses() {
check_str(
"`define FOO 4\n`define BAR (`FOO+$clog2(2))\n`BAR",
"(4+$clog2(2))",
);
}
#[test]
fn macro_name_with_digits_and_underscores() {
check_str("`define AXI_BUS21_SV 42\n`AXI_BUS21_SV", "42");
}
}