Skip to content

Commit 750fc2f

Browse files
committed
Tokenize tool directives
1 parent 5bcf513 commit 750fc2f

File tree

6 files changed

+165
-0
lines changed

6 files changed

+165
-0
lines changed

vhdl_lang/src/analysis/formal_region.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ impl<'a> std::ops::Deref for GpkgInterfaceEnt<'a> {
266266
GpkgInterfaceEnt::Type(typ) => typ.deref(),
267267
GpkgInterfaceEnt::Constant(obj) => obj.deref(),
268268
GpkgInterfaceEnt::Subprogram(subp) => subp.deref(),
269+
// `ent` is of type `&&AnyEnt`. `deref()` returns `&AnyEnt` which is what we want
270+
#[allow(suspicious_double_ref_op)]
269271
GpkgInterfaceEnt::Package(ent) => ent.deref(),
270272
}
271273
}

vhdl_lang/src/analysis/tests/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ mod resolves_names;
2121
mod resolves_type_mark;
2222
mod sensitivity_list;
2323
mod subprogram_arguments;
24+
mod tool_directive;
2425
mod typecheck_expression;
2526
mod util;
2627
mod visibility;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use crate::analysis::tests::{check_no_diagnostics, LibraryBuilder};
2+
3+
#[test]
4+
fn simple_tool_directive() {
5+
let mut builder = LibraryBuilder::new();
6+
builder.code("libname", "`protect begin");
7+
let (_, diagnostics) = builder.get_analyzed_root();
8+
9+
check_no_diagnostics(&diagnostics);
10+
}
11+
12+
#[test]
13+
fn tool_directive() {
14+
let mut builder = LibraryBuilder::new();
15+
builder.code(
16+
"libname",
17+
"\
18+
entity my_ent is
19+
end my_ent;
20+
21+
`protect begin_protected
22+
`protect version = 1
23+
`protect encrypt_agent = \"XILINX\"
24+
`protect encrypt_agent_info = \"Xilinx Encryption Tool 2020.2\"
25+
`protect key_keyowner = \"Cadence Design Systems.\", key_keyname = \"cds_rsa_key\", key_method = \"rsa\"
26+
`protect encoding = (enctype = \"BASE64\", line_length = 76, bytes = 64)
27+
",
28+
);
29+
let (_, diagnostics) = builder.get_analyzed_root();
30+
31+
check_no_diagnostics(&diagnostics);
32+
}

vhdl_lang/src/syntax/test.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,11 @@ fn substr_range(source: &Source, range: Range, substr: &str, occurence: usize) -
528528

529529
/// Fast forward tokenstream until position
530530
fn forward(stream: &TokenStream, start: Position) {
531+
// short-circuit when start is zero.
532+
// Also prevents the case where the token stream is empty
533+
if start.line == 0 && start.character == 0 {
534+
return;
535+
}
531536
loop {
532537
let token = stream.peek_expect().unwrap();
533538
if token.pos.start() >= start {

vhdl_lang/src/syntax/tokens/tokenizer.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ pub enum Kind {
164164
Comma,
165165
ColonEq,
166166
RightArrow,
167+
GraveAccent, // `
168+
Text, // Raw text that is not processed (i.e. tokenized) further. Used in tool directives
167169
}
168170
use self::Kind::*;
169171

@@ -413,6 +415,8 @@ pub fn kind_str(kind: Kind) -> &'static str {
413415
Comma => ",",
414416
ColonEq => ":=",
415417
RightArrow => "=>",
418+
GraveAccent => "`",
419+
Text => "{text}",
416420
}
417421
}
418422

@@ -442,6 +446,8 @@ pub enum Value {
442446
BitString(ast::BitString),
443447
AbstractLiteral(ast::AbstractLiteral),
444448
Character(u8),
449+
// Raw text that is not processed (i.e. tokenized) further. Used in tool directives
450+
Text(Latin1String),
445451
NoValue,
446452
}
447453

@@ -468,6 +474,7 @@ pub struct Comment {
468474
}
469475

470476
use std::convert::AsRef;
477+
471478
impl AsRef<SrcPos> for Token {
472479
fn as_ref(&self) -> &SrcPos {
473480
&self.pos
@@ -1149,6 +1156,25 @@ fn parse_character_literal(
11491156
}
11501157
}
11511158

1159+
/// Reads into `buffer` until a newline character is observed.
1160+
/// Does not consume the newline character.
1161+
///
1162+
/// Clears the buffer prior to reading
1163+
fn read_until_newline(
1164+
buffer: &mut Latin1String,
1165+
reader: &mut ContentReader,
1166+
) -> Result<(), TokenError> {
1167+
buffer.bytes.clear();
1168+
while let Some(b) = reader.peek()? {
1169+
if b == b'\n' {
1170+
break;
1171+
}
1172+
buffer.bytes.push(b);
1173+
reader.skip();
1174+
}
1175+
Ok(())
1176+
}
1177+
11521178
fn get_leading_comments(reader: &mut ContentReader) -> Result<Vec<Comment>, TokenError> {
11531179
let mut comments: Vec<Comment> = Vec::new();
11541180

@@ -1703,6 +1729,10 @@ impl<'a> Tokenizer<'a> {
17031729
let result = Value::Identifier(self.symbols.symtab().insert_extended(&result));
17041730
(Identifier, result)
17051731
}
1732+
b'`' => {
1733+
self.reader.skip();
1734+
(GraveAccent, Value::NoValue)
1735+
}
17061736
_ => {
17071737
self.reader.skip();
17081738
illegal_token!();
@@ -1764,6 +1794,25 @@ impl<'a> Tokenizer<'a> {
17641794
pub fn get_final_comments(&self) -> Option<Vec<Comment>> {
17651795
self.final_comments.clone()
17661796
}
1797+
1798+
pub fn text_until_newline(&mut self) -> DiagnosticResult<Token> {
1799+
let start_pos = self.reader.pos();
1800+
if let Err(err) = read_until_newline(&mut self.buffer, &mut self.reader) {
1801+
self.state.start = self.reader.state();
1802+
return Err(Diagnostic::error(
1803+
&self.source.pos(err.range.start, err.range.end),
1804+
err.message,
1805+
));
1806+
}
1807+
let text = self.buffer.clone();
1808+
let end_pos = self.reader.pos();
1809+
Ok(Token {
1810+
kind: Text,
1811+
value: Value::Text(text),
1812+
pos: self.source.pos(start_pos, end_pos),
1813+
comments: None,
1814+
})
1815+
}
17671816
}
17681817

17691818
#[cfg(test)]

vhdl_lang/src/syntax/tokens/tokenstream.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,49 @@ pub struct TokenStream<'a> {
1919
}
2020

2121
impl<'a> TokenStream<'a> {
22+
/// Special handling for a tool directive of the form
23+
/// ```vhdl
24+
/// `identifier { any chars until newline }
25+
/// ```
26+
/// Since what follows the identifier can be anything, this needs special handling.
27+
///
28+
/// Returns the tokens that make up the tool directive for processing of the tool directive.
29+
fn handle_tool_directive(
30+
grave_accent: Token,
31+
tokenizer: &mut Tokenizer,
32+
diagnostics: &mut dyn DiagnosticHandler,
33+
) {
34+
let start_pos = grave_accent.pos.clone();
35+
match tokenizer.pop() {
36+
Ok(Some(tok)) => {
37+
if tok.kind != Identifier {
38+
diagnostics.error(tok, "Expecting identifier");
39+
let _ = tokenizer.text_until_newline(); // skip potentially invalid tokens
40+
return;
41+
}
42+
}
43+
Err(err) => diagnostics.push(err),
44+
Ok(None) => {
45+
diagnostics.error(start_pos, "Expecting identifier");
46+
return;
47+
}
48+
}
49+
match tokenizer.text_until_newline() {
50+
Ok(_) => {}
51+
Err(err) => diagnostics.push(err),
52+
}
53+
}
54+
2255
pub fn new(
2356
mut tokenizer: Tokenizer<'a>,
2457
diagnostics: &mut dyn DiagnosticHandler,
2558
) -> TokenStream<'a> {
2659
let mut tokens = Vec::new();
2760
loop {
2861
match tokenizer.pop() {
62+
Ok(Some(token)) if token.kind == GraveAccent => {
63+
TokenStream::handle_tool_directive(token, &mut tokenizer, diagnostics)
64+
}
2965
Ok(Some(token)) => tokens.push(token),
3066
Ok(None) => break,
3167
Err(err) => diagnostics.push(err),
@@ -259,6 +295,12 @@ mod tests {
259295
let tokenizer = Tokenizer::new(&$code.symbols, source, ContentReader::new(&contents));
260296
let $stream = TokenStream::new(tokenizer, &mut NoDiagnostics);
261297
};
298+
($code:ident, $stream:ident, $diagnostics:ident) => {
299+
let source = $code.source();
300+
let contents = source.contents();
301+
let tokenizer = Tokenizer::new(&$code.symbols, source, ContentReader::new(&contents));
302+
let $stream = TokenStream::new(tokenizer, &mut $diagnostics);
303+
};
262304
}
263305

264306
#[test]
@@ -388,4 +430,38 @@ mod tests {
388430
assert!(stream.skip_until(|ref k| matches!(k, Plus)).is_ok());
389431
assert_eq!(stream.peek().map(|t| t.kind), Some(Plus));
390432
}
433+
434+
#[test]
435+
fn tokenize_simple_identifier_directive() {
436+
let code = Code::new("`protect begin");
437+
new_stream!(code, _stream);
438+
}
439+
440+
#[test]
441+
fn tokenize_extended_identifier_directive() {
442+
let code = Code::new("`\\extended ident\\ begin other words");
443+
new_stream!(code, _stream);
444+
}
445+
446+
#[test]
447+
fn tokenize_directive_illegal_identifier() {
448+
let code = Code::new("`123 begin other words");
449+
let mut diagnostics: Vec<Diagnostic> = vec![];
450+
new_stream!(code, _stream, diagnostics);
451+
assert_eq!(
452+
diagnostics,
453+
vec![Diagnostic::error(code.s1("123"), "Expecting identifier")]
454+
)
455+
}
456+
457+
#[test]
458+
fn tokenize_directive_then_end_of_stream() {
459+
let code = Code::new("`");
460+
let mut diagnostics: Vec<Diagnostic> = vec![];
461+
new_stream!(code, _stream, diagnostics);
462+
assert_eq!(
463+
diagnostics,
464+
vec![Diagnostic::error(code.s1("`"), "Expecting identifier")]
465+
)
466+
}
391467
}

0 commit comments

Comments
 (0)