Skip to content

Commit 6681f48

Browse files
authored
Merge pull request #188 from Schottkyc137/179-tool-directives
Feat: Tokenize tool directives
2 parents c3409d9 + 3de6d39 commit 6681f48

File tree

5 files changed

+161
-0
lines changed

5 files changed

+161
-0
lines changed

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: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,47 @@ 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+
/// This needs special handling as the text that follows the identifier is arbitrary.
27+
fn handle_tool_directive(
28+
grave_accent: Token,
29+
tokenizer: &mut Tokenizer,
30+
diagnostics: &mut dyn DiagnosticHandler,
31+
) {
32+
let start_pos = grave_accent.pos.clone();
33+
match tokenizer.pop() {
34+
Ok(Some(tok)) => {
35+
if tok.kind != Identifier {
36+
diagnostics.error(tok, "Expecting identifier");
37+
let _ = tokenizer.text_until_newline(); // skip potentially invalid tokens
38+
return;
39+
}
40+
}
41+
Err(err) => diagnostics.push(err),
42+
Ok(None) => {
43+
diagnostics.error(start_pos, "Expecting identifier");
44+
return;
45+
}
46+
}
47+
match tokenizer.text_until_newline() {
48+
Ok(_) => {}
49+
Err(err) => diagnostics.push(err),
50+
}
51+
}
52+
2253
pub fn new(
2354
mut tokenizer: Tokenizer<'a>,
2455
diagnostics: &mut dyn DiagnosticHandler,
2556
) -> TokenStream<'a> {
2657
let mut tokens = Vec::new();
2758
loop {
2859
match tokenizer.pop() {
60+
Ok(Some(token)) if token.kind == GraveAccent => {
61+
TokenStream::handle_tool_directive(token, &mut tokenizer, diagnostics)
62+
}
2963
Ok(Some(token)) => tokens.push(token),
3064
Ok(None) => break,
3165
Err(err) => diagnostics.push(err),
@@ -259,6 +293,12 @@ mod tests {
259293
let tokenizer = Tokenizer::new(&$code.symbols, source, ContentReader::new(&contents));
260294
let $stream = TokenStream::new(tokenizer, &mut NoDiagnostics);
261295
};
296+
($code:ident, $stream:ident, $diagnostics:ident) => {
297+
let source = $code.source();
298+
let contents = source.contents();
299+
let tokenizer = Tokenizer::new(&$code.symbols, source, ContentReader::new(&contents));
300+
let $stream = TokenStream::new(tokenizer, &mut $diagnostics);
301+
};
262302
}
263303

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

0 commit comments

Comments
 (0)