use crate::*;
use scarf_syntax::SpanRelation;
use std::collections::HashMap;
use std::io;
use std::path::{Path, PathBuf};
const DEFAULT_TIMESCALE: Timescale = Timescale::new_default(
(TimescaleValue::One, TimescaleUnit::NS),
(TimescaleValue::One, TimescaleUnit::NS),
);
const DEFAULT_NETTYPE: DefaultNettype = DefaultNettype::Wire;
const DEFAULT_UNCONNECTED_DRIVE: UnconnectedDrive =
UnconnectedDrive::NoUnconnected;
#[derive(Clone, Debug)]
pub struct Define<'a> {
pub name: SpannedString<'a>,
pub body: DefineBody<'a>,
}
impl<'a> Define<'a> {
pub fn is_from_command_line(&self) -> bool {
self.name.1.file == ""
}
}
#[derive(Clone, Debug)]
pub enum DefineBody<'a> {
Empty,
Text(Vec<SpannedToken<'a>>),
Function(DefineFunction<'a>),
}
#[derive(Clone, Debug)]
pub struct DefineFunction<'a> {
pub args: Vec<(
SpannedString<'a>,
Option<(
Span<'a>, // =
Vec<SpannedToken<'a>>,
)>,
)>,
pub body: Option<Vec<SpannedToken<'a>>>,
}
impl<'a> DefineBody<'a> {
pub fn get_tokens(
&self,
) -> (
Vec<SpannedToken<'a>>,
Option<Vec<(SpannedString<'a>, Option<Vec<SpannedToken<'a>>>)>>,
) {
match self {
DefineBody::Empty => (vec![], None),
DefineBody::Text(token_vec) => (token_vec.clone(), None),
DefineBody::Function(def_func) => {
let function_args = def_func
.args
.iter()
.map(|(a, b)| match b {
Some((_, tokens)) => (a.clone(), Some(tokens.clone())),
None => (a.clone(), None),
})
.collect();
match &def_func.body {
Some(token_vec) => (token_vec.clone(), Some(function_args)),
None => (vec![], Some(function_args)),
}
}
}
}
}
#[derive(Clone)]
pub struct LineDirective<'a> {
pub directive_file_name: &'a str,
pub directive_line_number: usize,
pub original_span: Span<'a>,
pub original_line_num: usize,
}
#[derive(Clone)]
pub struct PreprocessorState<'a> {
pub includes: Vec<&'a Path>,
pub defines: Vec<Define<'a>>,
pub timescales: Vec<Timescale<'a>>,
pub default_nettypes: Vec<(DefaultNettype, Span<'a>)>,
pub unconnected_drives: Vec<(UnconnectedDrive, Span<'a>)>,
pub cell_defines: Vec<(bool, Span<'a>)>,
pub line_directives: Vec<LineDirective<'a>>,
pub included_files: HashMap<&'a str, &'a str>,
pub curr_standard: StandardVersion,
pub warnings: Vec<PreprocessorWarning<'a>>,
pub(crate) in_define: bool,
pub(crate) in_define_arg: bool,
pub(crate) include_history: Vec<Span<'a>>,
}
impl<'a> PreprocessorState<'a> {
pub fn new(includes: Vec<&'a Path>, defines: Vec<Define<'a>>) -> Self {
Self {
includes,
defines,
timescales: vec![],
default_nettypes: vec![],
unconnected_drives: vec![],
cell_defines: vec![],
line_directives: vec![],
included_files: HashMap::new(),
curr_standard: StandardVersion::default(),
warnings: vec![],
in_define: false,
in_define_arg: false,
include_history: vec![],
}
}
pub fn make_fresh(&mut self, defines: Vec<Define<'a>>) {
self.defines = defines;
self.timescales = vec![];
self.default_nettypes = vec![];
self.unconnected_drives = vec![];
self.cell_defines = vec![];
self.line_directives = vec![];
self.curr_standard = StandardVersion::default();
self.warnings = vec![];
self.in_define = false;
self.in_define_arg = false;
self.include_history = vec![];
}
pub fn reset_all(&mut self, reset_all_span: Span<'a>) {
self.add_timescale(
Timescale::new(
reset_all_span.clone(),
DEFAULT_TIMESCALE.unit,
DEFAULT_TIMESCALE.precision,
)
.unwrap(),
);
self.add_default_nettype(reset_all_span.clone(), DEFAULT_NETTYPE);
self.add_unconnected_drive(
reset_all_span.clone(),
DEFAULT_UNCONNECTED_DRIVE,
);
self.add_cell_define(false, reset_all_span);
}
pub fn is_defined(&self, macro_name: &'a str) -> bool {
self.defines.iter().any(|d| d.name.0 == macro_name)
}
pub fn get_define_decl(&self, macro_name: &'a str) -> Option<Span<'a>> {
match self.defines.iter().find(|d| d.name.0 == macro_name) {
None => None,
Some(define) => Some(define.name.1.clone()),
}
}
#[inline]
pub(crate) fn enter_define(&mut self) -> bool {
let prev_in_define = self.in_define;
self.in_define = true;
prev_in_define
}
#[inline]
pub(crate) fn exit_define(&mut self, prev_in_define: bool) {
self.in_define = prev_in_define;
}
#[inline]
pub fn in_define(&self) -> bool {
self.in_define
}
#[inline]
pub(crate) fn enter_define_arg(&mut self) -> bool {
let prev_in_define_arg = self.in_define_arg;
self.in_define_arg = true;
prev_in_define_arg
}
#[inline]
pub(crate) fn exit_define_arg(&mut self, prev_in_define_arg: bool) {
self.in_define_arg = prev_in_define_arg;
}
#[inline]
pub fn in_define_arg(&self) -> bool {
self.in_define_arg
}
pub fn undefine(&mut self, macro_name: &'a str) -> bool {
let prev_len = self.defines.len();
self.defines.retain(|d| d.name.0 != macro_name);
prev_len != self.defines.len()
}
pub fn define(
&mut self,
macro_name: &'a str,
macro_span: Span<'a>,
macro_body: DefineBody<'a>,
) {
self.defines.push(Define {
name: SpannedString(macro_name, macro_span),
body: macro_body,
});
}
pub fn command_line_define(
&mut self,
macro_name: &'a str,
macro_text: Option<Vec<SpannedToken<'a>>>,
) {
self.define(
macro_name,
Span::default(),
match macro_text {
None => DefineBody::Empty,
Some(token_vec) => DefineBody::Text(token_vec),
},
)
}
pub fn undefineall(&mut self) {
self.defines = vec![];
}
pub fn get_macro_tokens(
&self,
macro_name: &'a str,
) -> Option<(
Span<'a>,
(
Vec<SpannedToken<'a>>,
Option<Vec<(SpannedString<'a>, Option<Vec<SpannedToken<'a>>>)>>,
),
)> {
for define in &self.defines {
if define.name.0 == macro_name {
return Some((define.name.1.clone(), define.body.get_tokens()));
}
}
None
}
pub fn get_file_path(&self, include_path: &str) -> Option<PathBuf> {
for dir_path in &self.includes {
let full_path = Path::new(dir_path).join(include_path);
if full_path.exists() {
return Some(full_path);
}
}
Some(PathBuf::from(include_path))
}
pub fn add_timescale(&mut self, timescale: Timescale<'a>) {
self.timescales.push(timescale);
}
pub fn get_timescale(&self, span: &Span<'a>) -> &Timescale<'a> {
for timescale in self.timescales.iter().rev() {
if timescale.is_valid(span) {
return timescale;
}
}
&DEFAULT_TIMESCALE
}
pub fn add_default_nettype(
&mut self,
def_span: Span<'a>,
default_nettype: DefaultNettype,
) {
self.default_nettypes.push((default_nettype, def_span));
}
pub fn get_default_nettype(&self, span: &Span<'a>) -> &DefaultNettype {
for default_nettype in self.default_nettypes.iter().rev() {
if default_nettype.1.compare(span) == SpanRelation::Earlier {
return &default_nettype.0;
}
}
&DEFAULT_NETTYPE
}
pub(crate) fn retain_include_file(
&mut self,
include_path: &'a str,
include_path_span: Span<'a>,
cache: &'a PreprocessorCache<'a>,
) -> Result<(&'a str, &'a str), PreprocessorError<'a>> {
let include_path_buf =
self.get_file_path(include_path).ok_or_else(|| {
PreprocessorError::Include(
include_path_span.clone(),
include_path.to_string(),
io::Error::new(io::ErrorKind::NotFound, "File not found"),
)
})?;
match self
.included_files
.get_key_value::<str>(include_path_buf.to_str().unwrap())
{
Some((path, contents)) => Ok((*path, *contents)),
None => {
let cached_path = cache.retain_string(
include_path_buf.to_str().unwrap().to_owned(),
);
let file_contents = std::fs::read_to_string(&cached_path)
.map_err(|err| {
PreprocessorError::Include(
include_path_span,
include_path.to_string(),
err,
)
})?;
let cached_contents = cache.retain_string(file_contents);
self.included_files.insert(cached_path, cached_contents);
Ok((cached_path, cached_contents))
}
}
}
pub fn retain_file(
&mut self,
file_path: String,
file_contents: String,
cache: &'a PreprocessorCache<'a>,
) -> (&'a str, &'a str) {
match self.included_files.get_key_value::<str>(file_path.as_ref()) {
Some((path, contents)) => (*path, *contents),
None => {
let path = cache.retain_string(file_path);
let contents = cache.retain_string(file_contents);
self.included_files.insert(path, contents);
(path, contents)
}
}
}
pub fn included_files(&self) -> Vec<(String, String)> {
self.included_files
.iter()
.map(|(a, b)| (a.to_string(), b.to_string()))
.collect()
}
pub fn add_unconnected_drive(
&mut self,
unconnected_drive_span: Span<'a>,
unconnected_drive: UnconnectedDrive,
) {
self.unconnected_drives
.push((unconnected_drive, unconnected_drive_span));
}
pub fn get_unconnected_drive(&self, span: &Span<'a>) -> &UnconnectedDrive {
for unconnected_drive in self.unconnected_drives.iter().rev() {
if unconnected_drive.1.compare(span) == SpanRelation::Earlier {
return &unconnected_drive.0;
}
}
&DEFAULT_UNCONNECTED_DRIVE
}
pub fn add_cell_define(&mut self, is_cell_define: bool, span: Span<'a>) {
self.cell_defines.push((is_cell_define, span));
}
pub fn is_cell_module(&self, declaration_span: &Span<'a>) -> bool {
for cell_define in self.cell_defines.iter().rev() {
if cell_define.1.compare(declaration_span) == SpanRelation::Earlier
{
return cell_define.0;
}
}
false
}
pub fn add_line_directive(
&mut self,
file_name: &'a str,
line_number: &'a str,
dir_span: Span<'a>,
) {
let offset = dir_span.bytes.end;
let file_contents: &str =
self.included_files.get(dir_span.file).unwrap();
let line_num = file_contents[..offset].lines().count();
let new_line_directive = LineDirective {
directive_file_name: file_name,
directive_line_number: line_number.parse().unwrap(),
original_span: dir_span,
original_line_num: line_num,
};
self.line_directives.push(new_line_directive);
}
pub fn get_line_directive_file(&self, span: &Span<'a>) -> &'a str {
let Some(line_directive) = self
.line_directives
.iter()
.rev()
.filter(|line_directive| {
(line_directive.original_span.file == span.file)
&& (line_directive.original_span.bytes.start
< span.bytes.start) })
.next()
else {
return span.file;
};
line_directive.directive_file_name
}
pub fn get_line_directive_line(
&mut self,
span: &Span<'a>,
cache: &'a PreprocessorCache<'a>,
) -> &'a str {
let offset = span.bytes.end;
let file_contents: &str = self.included_files.get(span.file).unwrap();
let line_num = file_contents[..offset].lines().count();
let Some(line_directive) = self
.line_directives
.iter()
.rev()
.filter(|line_directive| {
(line_directive.original_span.file == span.file)
&& (line_directive.original_span.bytes.start
< span.bytes.start) })
.next()
else {
return cache.retain_string(line_num.to_string());
};
let new_line_num = (line_num + line_directive.directive_line_number)
- (line_directive.original_line_num + 1);
cache.retain_string(new_line_num.to_string())
}
pub(crate) fn get_slice(&self, span: &Span<'a>) -> Option<&'a str> {
let file_contents: &str = self.included_files.get(span.file)?;
Some(&file_contents[span.bytes.start..span.bytes.end])
}
pub fn retain_string(
&mut self,
string: String,
cache: &'a PreprocessorCache<'a>,
) -> &'a str {
cache.retain_string(string)
}
pub fn warn(&mut self, warning: PreprocessorWarning<'a>) {
self.warnings.push(warning);
}
}