Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions openvaf/basedb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,15 @@ impl SourceProvider for SourceProviderDelegate<'_> {
fn file_id(&self, path: VfsPath) -> FileId {
self.0.file_id(path)
}

fn allocate_virtual_file(&self, path: &str, contents: Arc<str>) -> FileId {
let path = VfsPath::new_virtual_path(path.to_owned());
let file_id = self.0.vfs().write().ensure_file_id(path);
self.0.vfs().write().set_file_contents(file_id, contents.to_string().into());
// Ensure subsequent salsa `file_text` reads see the contents immediately.
// Preprocess already mutates VFS via `file_id` / includes; this matches that pattern.
file_id
}
}

#[macro_export]
Expand Down
24 changes: 15 additions & 9 deletions openvaf/preprocessor/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,15 +212,21 @@ fn parse_macro_token<'a>(
}

if p.at(PreprocessorToken::CompilerDirective) {
if p.compiler_directive() == CompilerDirective::Macro {
let (call, range) = parse_macro_call(p, err, args, sm, end);
dst.push(ParsedToken { range, kind: ParsedTokenKind::MacroCall(call) });
} else {
// TODO nicer error?
err.push(PreprocessorDiagnostic::UnexpectedToken(CtxSpan {
ctx: p.ctx,
range: p.current_range(),
}))
match p.compiler_directive() {
// `` `__FILE__ `` / `` `__LINE__ `` must be stored like macros so they
// expand at the call site of the enclosing `` `define ``. Treating them
// as unexpected without bumping the parser would spin forever.
CompilerDirective::Macro | CompilerDirective::File | CompilerDirective::Line => {
let (call, range) = parse_macro_call(p, err, args, sm, end);
dst.push(ParsedToken { range, kind: ParsedTokenKind::MacroCall(call) });
}
_ => {
err.push(PreprocessorDiagnostic::UnexpectedToken(CtxSpan {
ctx: p.ctx,
range: p.current_range(),
}));
p.bump();
}
}
return;
}
Expand Down
5 changes: 5 additions & 0 deletions openvaf/preprocessor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ pub trait SourceProvider {
fn file_text(&self, file: FileId) -> Result<Arc<str>, FileReadError>;
fn file_path(&self, file: FileId) -> VfsPath;
fn file_id(&self, path: VfsPath) -> FileId;

/// Allocate a virtual file whose contents become the expansion text for a
/// preprocessor-generated token (e.g. `` `__FILE__ `` / `` `__LINE__ ``).
/// Token spans must point at real `FileId` text for the green tree builder.
fn allocate_virtual_file(&self, path: &str, contents: Arc<str>) -> FileId;
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
Expand Down
7 changes: 7 additions & 0 deletions openvaf/preprocessor/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ impl<'a, 'd> Parser<'a, 'd> {
"`endif" => CompilerDirective::EndIf,
"`undef" => CompilerDirective::Undef,
"`resetall" => CompilerDirective::ResetAll,
// VAMS-2023 §10.7 / IEEE 1364: expand to string / decimal literals.
"`__FILE__" => CompilerDirective::File,
"`__LINE__" => CompilerDirective::Line,
_ => CompilerDirective::Macro,
}
}
Expand Down Expand Up @@ -334,5 +337,9 @@ pub enum CompilerDirective {
EndIf,
Undef,
ResetAll,
/// `` `__FILE__ `` — expands to a string literal of the current input path.
File,
/// `` `__LINE__ `` — expands to a decimal literal of the current line number.
Line,
Macro,
}
93 changes: 92 additions & 1 deletion openvaf/preprocessor/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use ahash::AHashMap;
use stdx::{impl_debug_display, impl_idx_from};
use text_size::{TextRange, TextSize};
use tokens::parser::SyntaxKind;
use tokens::SyntaxKind::{L_PAREN, R_PAREN};
use tokens::SyntaxKind::{INT_NUMBER, L_PAREN, R_PAREN, STR_LIT};
// use tracing::{debug, debug_span, trace};
use typed_index_collections::{TiSlice, TiVec};
use vfs::{FileId, VfsPath};
Expand All @@ -25,6 +25,8 @@ pub(crate) struct Processor<'a> {
arena: &'a ScopedTextArea,
macros: AHashMap<&'a str, Macro<'a>>,
include_dirs: Arc<[VfsPath]>,
/// Monotonic id for virtual expansion files allocated for `` `__FILE__ `` / `` `__LINE__ ``.
expand_seq: u32,
}

impl<'a> Processor<'a> {
Expand All @@ -51,6 +53,7 @@ impl<'a> Processor<'a> {
arena: storage,
sources,
include_dirs: sources.include_dirs(root_file),
expand_seq: 0,
};
Ok(res)
}
Expand Down Expand Up @@ -149,6 +152,17 @@ impl<'a> Processor<'a> {
dst: &mut Vec<Token>,
errors: &mut Diagnostics,
) {
// `` `__FILE__ `` / `` `__LINE__ `` may appear inside `` `define `` bodies as
// nested macro calls; expand them at the nested call site.
if call.name == "__FILE__" {
self.expand_file_line(true, span, dst);
return;
}
if call.name == "__LINE__" {
self.expand_file_line(false, span, dst);
return;
}

// TODO track recursion
//
let parent_ctx_span = self.source_map.ctx_data(span.ctx).decl.range.start();
Expand Down Expand Up @@ -196,6 +210,53 @@ impl<'a> Processor<'a> {
}
}

/// Expand `` `__FILE__ `` (`is_file`) or `` `__LINE__ `` to a literal token whose
/// text lives in a freshly allocated virtual file (green-tree text is always
/// sliced from a `FileId`).
fn expand_file_line(&mut self, is_file: bool, call_site: CtxSpan, dst: &mut Vec<Token>) {
// Direct uses (and uses inside `` `include ``d files) report the current
// input file. When expanding from a user-macro body the context decl is a
// subrange of a file, and we report the macro invocation site instead so
// idioms like `` `define LOC `__FILE__, `__LINE__ `` are useful.
let loc_span = {
let ctx_data = self.source_map.ctx_data(call_site.ctx);
let decl = ctx_data.decl;
let whole_file = self
.sources
.file_text(decl.file)
.ok()
.map(|src| decl.range == TextRange::up_to(TextSize::of(&*src)))
.unwrap_or(true);
if !whole_file {
ctx_data.call_site.unwrap_or(call_site)
} else {
call_site
}
};
let filespan = loc_span.to_file_span(&self.source_map);
let lit_text: Arc<str> = if is_file {
let path = self.sources.file_path(filespan.file);
format!("\"{}\"", escape_pp_string(&path.to_string())).into()
} else {
let src =
self.sources.file_text(filespan.file).expect("SourceContext file must be readable");
let line = line_number_1based(&src, filespan.range.start());
line.to_string().into()
};

let seq = self.expand_seq;
self.expand_seq = seq.wrapping_add(1);
let virt_path = format!("/<pp-expand>/{}/{}", if is_file { "file" } else { "line" }, seq);
let file = self.sources.allocate_virtual_file(&virt_path, lit_text.clone());
let lit_text = self.arena.ensure(lit_text);
let range = TextRange::up_to(TextSize::of(lit_text));
let ctx = self.source_map.add_ctx(FileSpan { file, range }, call_site);
dst.push(Token {
kind: if is_file { STR_LIT } else { INT_NUMBER },
span: CtxSpan { range, ctx },
});
}

pub(crate) fn process_file(&mut self, mut p: Parser<'a, '_>, err: &mut Diagnostics) {
while !p.at(PreprocessorToken::Eof) {
self.process_token(&mut p, err)
Expand Down Expand Up @@ -268,6 +329,16 @@ impl<'a> Processor<'a> {
});
p.bump();
}
CompilerDirective::File => {
let span = p.current_span();
p.bump();
self.expand_file_line(true, span, p.dst);
}
CompilerDirective::Line => {
let span = p.current_span();
p.bump();
self.expand_file_line(false, span, p.dst);
}
CompilerDirective::Macro => {
let (call, range) =
parse_macro_call(p, err, &[], &mut self.source_map, p.end());
Expand All @@ -286,6 +357,26 @@ impl<'a> Processor<'a> {
}
}

/// 1-based line number of `offset` in `src` (newlines before the offset).
fn line_number_1based(src: &str, offset: TextSize) -> u32 {
let idx: usize = offset.into();
let idx = idx.min(src.len());
1 + src[..idx].bytes().filter(|&b| b == b'\n').count() as u32
}

/// Escape a path for embedding inside a Verilog string literal.
fn escape_pp_string(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for c in s.chars() {
match c {
'\\' => out.push_str("\\\\"),
'"' => out.push_str("\\\""),
c => out.push(c),
}
}
out
}

pub(crate) type MacroArgs<'s> = TiVec<MacroArg, (Vec<ParsedToken<'s>>, TextRange)>;

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
Expand Down
20 changes: 20 additions & 0 deletions openvaf/preprocessor/src/test_data/file_line_across_include.tokens
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
COMMENT
WHITESPACE
SYSFUN
L_PAREN
STR_LIT
COMMA
WHITESPACE
INT_NUMBER
R_PAREN
SEMICOLON
WHITESPACE
SYSFUN
L_PAREN
STR_LIT
COMMA
WHITESPACE
INT_NUMBER
R_PAREN
SEMICOLON
WHITESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// included
$display("/inc.va", 2);
$display("/parent.va", 2);
13 changes: 13 additions & 0 deletions openvaf/preprocessor/src/test_data/file_line_directives.tokens
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
WHITESPACE
SYSFUN
L_PAREN
STR_LIT
COMMA
WHITESPACE
STR_LIT
COMMA
WHITESPACE
INT_NUMBER
R_PAREN
SEMICOLON
WHITESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

$display("at %s:%d", "/macro_expansion_test.va", 2);
10 changes: 10 additions & 0 deletions openvaf/preprocessor/src/test_data/file_line_inside_define.tokens
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
WHITESPACE
SYSFUN
L_PAREN
STR_LIT
COMMA
WHITESPACE
INT_NUMBER
R_PAREN
SEMICOLON
WHITESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

$display("/macro_expansion_test.va", 3);
51 changes: 51 additions & 0 deletions openvaf/preprocessor/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ impl SourceProvider for TestSourceProvider {
fn file_id(&self, path: VfsPath) -> FileId {
self.vfs.borrow_mut().ensure_file_id(path)
}

fn allocate_virtual_file(&self, path: &str, contents: Arc<str>) -> FileId {
self.vfs.borrow_mut().add_virt_file(path, contents.to_string().into())
}
}

fn check_prepocessor(sources: TestSourceProvider, root_file: FileId, test_name: &'static str) {
Expand Down Expand Up @@ -181,3 +185,50 @@ fn source_map_triple_replacement() {
"source_map_triple_replacement",
)
}

/// VAMS-2023 §10.7: `` `__FILE__ `` / `` `__LINE__ `` expand to string / decimal
/// literals of the current input file and line.
#[test]
fn file_line_directives() {
// Keep the directives on known lines so the expanded decimals are stable.
// Line 1 is blank after the raw-string newline; line 2 is the display call.
check_prepocessor_single_file(
r#"
$display("at %s:%d", `__FILE__, `__LINE__);
"#,
"file_line_directives",
)
}

/// After `` `include ``, `` `__FILE__ `` / `` `__LINE__ `` must report the included
/// file; once the include ends they revert to the parent.
#[test]
fn file_line_across_include() {
let sources = TestSourceProvider::new(vec![]);
let root = {
let mut vfs = sources.vfs.borrow_mut();
vfs.add_virt_file(
"/inc.va",
concat!("// included\n", "$display(`__FILE__, `__LINE__);\n").to_owned().into(),
);
vfs.add_virt_file(
"/parent.va",
concat!("`include \"inc.va\"\n", "$display(`__FILE__, `__LINE__);\n")
.to_owned()
.into(),
)
};
check_prepocessor(sources, root, "file_line_across_include");
}

/// Nested appearance inside a `` `define `` body expands at the call site.
#[test]
fn file_line_inside_define() {
check_prepocessor_single_file(
r#"
`define LOC `__FILE__, `__LINE__
$display(`LOC);
"#,
"file_line_inside_define",
)
}
Loading