diff --git a/sv-parser-parser/src/keywords.rs b/sv-parser-parser/src/keywords.rs index 36285c9..52442fe 100644 --- a/sv-parser-parser/src/keywords.rs +++ b/sv-parser-parser/src/keywords.rs @@ -1446,8 +1446,6 @@ pub(crate) const KEYWORDS_1800_2017: &[&str] = &[ ]; pub(crate) const KEYWORDS_DIRECTIVE: &[&str] = &[ - "__FILE__", - "__LINE__", "begin_keywords", "celldefine", "default_nettype", diff --git a/sv-parser-pp/src/preprocess.rs b/sv-parser-pp/src/preprocess.rs index 2e5e739..0093903 100644 --- a/sv-parser-pp/src/preprocess.rs +++ b/sv-parser-pp/src/preprocess.rs @@ -124,6 +124,25 @@ pub fn preprocess, U: AsRef, V: BuildHasher>( strip_comments: bool, ignore_include: bool, ) -> Result<(PreprocessedText, Defines), Error> { + preprocess_inner( + path, + pre_defines, + include_paths, + strip_comments, + ignore_include, + 0, // include_depth + ) +} + +fn preprocess_inner, U: AsRef, V: BuildHasher>( + path: T, + pre_defines: &Defines, + include_paths: &[U], + strip_comments: bool, + ignore_include: bool, + include_depth: usize, +) -> Result<(PreprocessedText, Defines), Error> { + let f = File::open(path.as_ref()).map_err(|x| Error::File { source: x, path: PathBuf::from(path.as_ref()), @@ -139,7 +158,8 @@ pub fn preprocess, U: AsRef, V: BuildHasher>( include_paths, ignore_include, strip_comments, - 0, + 0, // resolve_depth + include_depth, ) } @@ -179,7 +199,19 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( ignore_include: bool, strip_comments: bool, resolve_depth: usize, + include_depth: usize, ) -> Result<(PreprocessedText, Defines), Error> { + + // IEEE1800-2017 Clause 22.4, page 675 + // A file included in the source using the `include compiler directive + // may contain other `include compiler directives. + // The number of nesting levels for include files shall be finite. + // Implementations may limit the maximum number of levels to which + // include files can be nested, but the limit shall be at least 15. + if include_depth > RECURSIVE_LIMIT { + return Err(Error::ExceedRecursiveLimit); + } + let mut skip = false; let mut skip_whitespace = false; let mut skip_nodes = SkipNodes::new(); @@ -260,6 +292,7 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( } _ => (), } + match n { NodeEvent::Enter(RefNode::SourceDescriptionNotDirective(x)) => { let locate: Locate = x.try_into().unwrap(); @@ -378,18 +411,28 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( skip_whitespace = false; } NodeEvent::Enter(RefNode::UndefineCompilerDirective(x)) => { - skip_nodes.push(x.into()); - skip = true; - let (_, _, ref name) = x.nodes; let id = identifier((&name.nodes.0).into(), &s).unwrap(); defines.remove(&id); + + let locate: Locate = x.try_into().unwrap(); + let range = Range::new(locate.offset, locate.offset + locate.len); + ret.push(locate.str(&s), Some((path.as_ref(), range))); + skip_whitespace = true; + } + NodeEvent::Leave(RefNode::UndefineCompilerDirective(_)) => { + skip_whitespace = false; } NodeEvent::Enter(RefNode::UndefineallCompilerDirective(x)) => { - skip_nodes.push(x.into()); - skip = true; - defines.clear(); + + let locate: Locate = x.try_into().unwrap(); + let range = Range::new(locate.offset, locate.offset + locate.len); + ret.push(locate.str(&s), Some((path.as_ref(), range))); + skip_whitespace = true; + } + NodeEvent::Leave(RefNode::UndefineallCompilerDirective(_)) => { + skip_whitespace = false; } NodeEvent::Enter(RefNode::IfdefDirective(x)) => { let (_, ref keyword, ref ifid, ref ifbody, ref elsif, ref elsebody, _, _) = x.nodes; @@ -398,7 +441,7 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( let ifid = identifier(ifid.into(), &s).unwrap(); let mut hit = false; - if defines.contains_key(&ifid) { + if defines.contains_key(&ifid) || is_predefined_text_macro(&ifid) { hit = true; } else { skip_nodes.push(ifbody.into()); @@ -412,7 +455,7 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( let elsifid = identifier(elsifid.into(), &s).unwrap(); if hit { skip_nodes.push(elsifbody.into()); - } else if defines.contains_key(&elsifid) { + } else if defines.contains_key(&elsifid) || is_predefined_text_macro(&ifid) { hit = true; } else { skip_nodes.push(elsifbody.into()); @@ -446,7 +489,7 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( let ifid = identifier(ifid.into(), &s).unwrap(); let mut hit = false; - if !defines.contains_key(&ifid) { + if !defines.contains_key(&ifid) && !is_predefined_text_macro(&ifid) { hit = true; } else { skip_nodes.push(ifbody.into()); @@ -460,7 +503,7 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( let elsifid = identifier(elsifid.into(), &s).unwrap(); if hit { skip_nodes.push(elsifbody.into()); - } else if defines.contains_key(&elsifid) { + } else if defines.contains_key(&elsifid) || is_predefined_text_macro(&ifid) { hit = true; } else { skip_nodes.push(elsifbody.into()); @@ -483,48 +526,50 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( let (ref name, ref args) = proto.nodes; let id = identifier(name.into(), &s).unwrap(); - let mut define_args = Vec::new(); - if let Some(args) = args { - let (_, ref args, _) = args.nodes; - let (ref args,) = args.nodes; - for arg in args.contents() { - let (ref arg, ref default) = arg.nodes; - let (ref arg, _) = arg.nodes; - let arg = String::from(arg.str(&s)); + if !is_predefined_text_macro(id.as_str()) { + let mut define_args = Vec::new(); + if let Some(args) = args { + let (_, ref args, _) = args.nodes; + let (ref args,) = args.nodes; + for arg in args.contents() { + let (ref arg, ref default) = arg.nodes; + let (ref arg, _) = arg.nodes; + let arg = String::from(arg.str(&s)); - let default = if let Some((_, x)) = default { - let x: Locate = x.try_into().unwrap(); - let x = String::from(x.str(&s)); - Some(x) - } else { - None - }; + let default = if let Some((_, x)) = default { + let x: Locate = x.try_into().unwrap(); + let x = String::from(x.str(&s)); + Some(x) + } else { + None + }; - define_args.push((arg, default)); + define_args.push((arg, default)); + } } + + let define_text = if let Some(text) = text { + let text: Locate = text.try_into().unwrap(); + let range = Range::new(text.offset, text.offset + text.len); + let text = String::from(text.str(&s)); + Some(DefineText { + text, + origin: Some((PathBuf::from(path.as_ref()), range)), + }) + } else { + None + }; + + let define = Define { + identifier: id.clone(), + arguments: define_args, + text: define_text, + }; + + defines.insert(id, Some(define)); } - let define_text = if let Some(text) = text { - let text: Locate = text.try_into().unwrap(); - let range = Range::new(text.offset, text.offset + text.len); - let text = String::from(text.str(&s)); - Some(DefineText { - text, - origin: Some((PathBuf::from(path.as_ref()), range)), - }) - } else { - None - }; - - let define = Define { - identifier: id.clone(), - arguments: define_args, - text: define_text, - }; - - defines.insert(id, Some(define)); - - // Keep TextMacroDefinition after preprocess + // Keep TextMacroDefinition after preprocess_inner(). let locate: Locate = x.try_into().unwrap(); let range = Range::new(locate.offset, locate.offset + locate.len); ret.push(locate.str(&s), Some((path.as_ref(), range))); @@ -536,6 +581,9 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( let locate: Locate = x.try_into().unwrap(); last_include_line = Some(locate.line); + // IEEE1800-2017 Clause 22.4, page 675 + // Only white space or a comment may appear on the same line as + // the `include compiler directive. if let Some(last_item_line) = last_item_line { if last_item_line == locate.line { return Err(Error::IncludeLine); @@ -580,6 +628,20 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( } } }; + + // IEEE1800-2017 Clause 22.4, page 675 + // The filename can be enclosed in either quotes or angle brackets, + // which affects how a tool searches for the file, as follows: + // - When the filename is enclosed in double quotes ("filename"), for + // a relative path the compiler’s current working directory, and + // optionally user-specified locations are searched. + // - When the filename is enclosed in angle brackets (), then + // only an implementationdependent location containing files defined + // by the language standard is searched. Relative path names are + // interpreted relative to that location + // + // In this implementation, filenames enclosed in angle brackets are + // treated equivalently to those enclosed in double quotes. if path.is_relative() && !path.exists() { for include_path in include_paths { let new_path = include_path.as_ref().join(&path); @@ -589,8 +651,15 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( } } } + let (include, new_defines) = - preprocess(path, &defines, include_paths, strip_comments, false).map_err( + preprocess_inner( + path, + &defines, + include_paths, + strip_comments, + false, // ignore_include + include_depth + 1).map_err( |x| Error::Include { source: Box::new(x), }, @@ -614,6 +683,40 @@ pub fn preprocess_str, U: AsRef, V: BuildHasher>( ret.push(&text, origin); defines = new_defines; } + + // Push the trailing whitespace attached to either + // TextMacroIdentifier or Option>. + let (ref _symbol, ref id, ref args) = x.nodes; + match args { + Some(p) => { + // Arguments given to macro in parentheses. + let (ref _opening, ref _args, ref closing) = p.nodes; + for x in closing { + match x { + RefNode::WhiteSpace(x) => { + let locate: Locate = x.try_into().unwrap(); + let range = Range::new(locate.offset, locate.offset + locate.len); + ret.push(locate.str(&s), Some((path.as_ref(), range))); + } + _ => { + } + } + } + } + None => { + // No arguments given to macro. + for x in id { + match x { + RefNode::WhiteSpace(x) => { + let locate: Locate = x.try_into().unwrap(); + let range = Range::new(locate.offset, locate.offset + locate.len); + ret.push(locate.str(&s), Some((path.as_ref(), range))); + } + _ => {} + } + } + } + } } NodeEvent::Enter(RefNode::PositionCompilerDirective(x)) => { skip_nodes.push(x.into()); @@ -673,17 +776,61 @@ fn get_str(node: RefNode, s: &str) -> String { ret } +fn is_predefined_text_macro(s: &str) -> bool { + match s { + "__LINE__" | "__FILE__" => { + true + } + _ => { + false + } + } +} + fn split_text(s: &str) -> Vec { let mut is_string = false; let mut is_ident = false; - let mut is_backquote_prev = false; - let mut is_comment = false; let mut is_ident_prev; let mut x = String::from(""); let mut ret = vec![]; + // IEEE1800-2017 Clause 22.5.1, page 676 + // If a one-line comment (that is, a comment specified with the + // characters //) is included in the text, then the comment shall not + // become part of the substituted text. + let mut is_comment = false; + + // IEEE1800-2017 Clause 22.5.1, page 680 + // An `" overrides the usual lexical meaning of " and indicates that the + // expansion shall include the quotation mark, substitution of actual + // arguments, and expansions of embedded macros. + // This allows string literals to be constructed from macro arguments. + let mut is_backquote_prev = false; + + let mut is_leading_whitespace = true; + let mut is_backslash_prev = false; + let mut iter = s.chars().peekable(); while let Some(c) = iter.next() { + + // IEEE1800-2017 Clause 22.5.1, page 676, Syntax 22-2. + // Ignore whitespace immediately after text_macro_name. + if is_leading_whitespace { + if c != '\\' && !c.is_ascii_whitespace() { + // Non-whitespace character, move onto main loop. + is_leading_whitespace = false; + } else if is_backslash_prev && c == '\n' { + // Drop the \n from leading continuation, then move onto main loop. + is_leading_whitespace = false; + continue; + } else { + // Still in leading whitespace or possible continuation. + // Detect possible continuation, then try next character. + is_backslash_prev = c == '\\'; + continue; + } + } + is_ident_prev = is_ident; is_ident = c.is_ascii_alphanumeric() | (c == '_'); @@ -805,12 +952,12 @@ fn resolve_text_macro_usage, U: AsRef>( } else { replaced.push_str( &text - .replace("``", "") - .replace("`\\`\"", "\\\"") - .replace("`\"", "\"") - .replace("\\\n", "\n") - .replace("\\\r\n", "\r\n") - .replace("\\\r", "\r"), + .replace("``", "") // Argument substitution. + .replace("`\\`\"", "\\\"") // Escaped backslash. + .replace("`\"", "\"") // Escaped quote. + .replace("\\\n", "\n") // Line continuation (Unix). + .replace("\\\r\n", "\r\n") // Line continuation (Windows). + .replace("\\\r", "\r"), // Line continuation (old Mac). ); } } @@ -819,10 +966,6 @@ fn resolve_text_macro_usage, U: AsRef>( replaced.push_str(&paren); } - // separator is required - replaced.push_str(" "); - // remove leading whitespace - replaced = String::from(replaced.trim_start()); let (replaced, new_defines) = preprocess_str( &replaced, path.as_ref(), @@ -831,6 +974,7 @@ fn resolve_text_macro_usage, U: AsRef>( false, strip_comments, resolve_depth, + 0, // include_depth )?; Ok(Some(( String::from(replaced.text()), @@ -852,7 +996,7 @@ mod tests { use super::*; use std::env; - fn get_testcase(s: &str) -> String { + fn testfile_path(s: &str) -> String { format!( "{}/testcases/{}", env::var("CARGO_MANIFEST_DIR").unwrap(), @@ -860,564 +1004,521 @@ mod tests { ) } + fn testfile_contents(s: &str) -> String { + let path: String = testfile_path(s); + + let file = File::open(path).unwrap(); + let mut buf_reader = BufReader::new(file); + let mut contents = String::new(); + buf_reader.read_to_string(&mut contents).unwrap(); + + contents + } + + // Most tests are called with the same arguments, so this is a convenience. + fn preprocess_usualargs(s: &str) -> Result<(PreprocessedText, Defines), Error> { + let include_paths = [testfile_path("")]; + preprocess( + testfile_path(s), // path + &HashMap::new(), // pre_defines + &include_paths, // include_paths + false, // strip_comments + false, // ignore_include + ) + } + #[test] - fn test1() { + fn escaped_identifier() { // {{{ + let (ret, _) = preprocess_usualargs("escaped_identifier.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/escaped_identifier.sv") + ); + } // }}} + + #[test] + #[allow(non_snake_case)] + fn IEEE18002017_keywords_if2_13642005() { // {{{ + let (ret, _) = preprocess_usualargs("IEEE18002017_keywords_if2_13642005.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/IEEE18002017_keywords_if2_13642005.sv") + ); + } // }}} + + #[test] + #[allow(non_snake_case)] + fn IEEE18002017_keywords_m2_13642001() { // {{{ + let (ret, _) = preprocess_usualargs("IEEE18002017_keywords_m2_13642001.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/IEEE18002017_keywords_m2_13642001.sv") + ); + } // }}} + + #[test] + #[allow(non_snake_case)] + fn IEEE18002017_keywords_m2_18002005() { // {{{ + let (ret, _) = preprocess_usualargs("IEEE18002017_keywords_m2_18002005.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/IEEE18002017_keywords_m2_18002005.sv") + ); + } // }}} + + #[test] + #[allow(non_snake_case)] + fn IEEE18002017_macro_argument_expansion() { // {{{ + let (ret, _) = preprocess_usualargs("IEEE18002017_macro_argument_expansion.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/IEEE18002017_macro_argument_expansion.sv") + ); + } // }}} + + #[test] + #[allow(non_snake_case)] + fn IEEE18002017_macro_delimit_tokens() { // {{{ + let (ret, _) = preprocess_usualargs("IEEE18002017_macro_delimit_tokens.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/IEEE18002017_macro_delimit_tokens.sv") + ); + } // }}} + + #[test] + #[allow(non_snake_case)] + fn IEEE18002017_macro_mix_quotes() { // {{{ + let (ret, _) = preprocess_usualargs("IEEE18002017_macro_mix_quotes.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/IEEE18002017_macro_mix_quotes.sv") + ); + } // }}} + + #[test] + #[allow(non_snake_case)] + fn IEEE18002017_macro_noexpand_string() { // {{{ + let (ret, _) = preprocess_usualargs("IEEE18002017_macro_noexpand_string.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/IEEE18002017_macro_noexpand_string.sv") + ); + } // }}} + + #[test] + #[allow(non_snake_case)] + fn IEEE18002017_macro_with_defaults() { // {{{ + let (ret, _) = preprocess_usualargs("IEEE18002017_macro_with_defaults.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/IEEE18002017_macro_with_defaults.sv") + ); + } // }}} + + #[test] + #[allow(non_snake_case)] + fn IEEE18002017_macro_without_defaults() { // {{{ + let (ret, _) = preprocess_usualargs("IEEE18002017_macro_without_defaults.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/IEEE18002017_macro_without_defaults.sv") + ); + } // }}} + + #[test] + fn celldefine() { // {{{ + let (ret, _) = preprocess_usualargs("celldefine.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("celldefine.sv") + ); + } // }}} + + #[test] + fn default_nettype() { // {{{ + let (ret, _) = preprocess_usualargs("default_nettype.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("default_nettype.sv") + ); + } // }}} + + #[test] + fn ifdef_nested() { // {{{ + let (ret, _) = preprocess_usualargs("ifdef_nested.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/ifdef_nested.sv") + ); + } // }}} + + #[test] + fn ifdef_predefined() { // {{{ + let mut defines = HashMap::new(); + defines.insert(String::from("behavioral"), None); let (ret, _) = preprocess( - get_testcase("test1.sv"), - &HashMap::new(), + testfile_path("ifdef_predefined.sv"), + &defines, &[] as &[String], - false, - false, + false, // strip_comments + false, // ignore_include ) .unwrap(); assert_eq!( ret.text(), - r##"module and_op (a, b, c); -output a; -input b, c; + testfile_contents("expected/ifdef_predefined.sv") + ) + } // }}} -and a1 (a,b,c); - -endmodule -"## + #[test] + fn ifdef_undefined() { // {{{ + let (ret, _) = preprocess_usualargs("ifdef_undefined.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/ifdef_undefined.sv") ); assert_eq!( ret.origin(10).unwrap().0, - &PathBuf::from(get_testcase("test1.sv")) + &PathBuf::from(testfile_path("ifdef_undefined.sv")) ); assert_eq!(ret.origin(10).unwrap().1, 10); assert_eq!(ret.origin(50).unwrap().1, 98); assert_eq!(ret.origin(70).unwrap().1, 124); - } + } // }}} #[test] - fn test1_predefine() { - let mut defines = HashMap::new(); - defines.insert(String::from("behavioral"), None); - let (ret, _) = preprocess( - get_testcase("test1.sv"), - &defines, - &[] as &[String], - false, - false, - ) - .unwrap(); + fn ifndef_undefined() { // {{{ + let (ret, _) = preprocess_usualargs("ifndef_undefined.sv").unwrap(); assert_eq!( ret.text(), - r##"module and_op (a, b, c); -output a; -input b, c; - -wire a = b & c; - -endmodule -"## - ) - } + testfile_contents("expected/ifndef_undefined.sv") + ); + } // }}} #[test] - fn test2() { - let include_paths = [get_testcase("")]; + fn include_ignore() { // {{{ + let include_paths = [testfile_path("")]; let (ret, _) = preprocess( - get_testcase("test2.sv"), + testfile_path("include_ignore.sv"), &HashMap::new(), &include_paths, - false, - false, + false, // strip_comments + true, // ignore_include ) .unwrap(); assert_eq!( ret.text(), - r##"module and_op (a, b, c); -output a; -input b, c; + testfile_contents("expected/include_ignore.sv") + ); + } // }}} -and a1 (a,b,c); + #[test] + fn include_noindent() { // {{{ + let (ret, _) = preprocess_usualargs("include_noindent.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/include_noindent.sv") + ); + // 11th char of returned text is '_' in the module identifier + // "and_op", and originates from the parent file. + // Characters are zero-indexed. + let n = 10; + assert_eq!( + ret.origin(n).unwrap(), + (&PathBuf::from(testfile_path("include_noindent.sv")), n) + ); + assert_eq!(ret.text().chars().nth(n).unwrap(), '_'); -endmodule -"## + // 51st char of returned text is 'd' in the primitive identifier + // "and", and originates from the child file at character index 72. + let n = 50; + assert_eq!( + ret.origin(n).unwrap(), + (&PathBuf::from(testfile_path("included.svh")), 73) + ); + assert_eq!(ret.text().chars().nth(n).unwrap(), 'd'); + + // 71st char of returned text is 'o' in the keword "endmodule", and + // originates from the parent file. + let n = 70; + assert_eq!( + ret.origin(n).unwrap(), + (&PathBuf::from(testfile_path("include_noindent.sv")), 53) + ); + assert_eq!(ret.text().chars().nth(n).unwrap(), 'o'); + } // }}} + + #[test] + fn include_quoted_a() { // {{{ + let ret = preprocess_usualargs("include_quoted_a.sv"); + assert_eq!(format!("{:?}", ret), "Err(Include { source: File { source: Os { code: 2, kind: NotFound, message: \"No such file or directory\" }, path: \"`PATH\" } })"); + } // }}} + + #[test] + fn include_quoted_b() { // {{{ + let ret = preprocess_usualargs("include_quoted_b.sv"); + assert_eq!(format!("{:?}", ret), "Err(Include { source: File { source: Os { code: 2, kind: NotFound, message: \"No such file or directory\" }, path: \"`PATH\" } })"); + } // }}} + + #[test] + fn include_quoted_c() { // {{{ + let (ret, _) = preprocess_usualargs("include_quoted_c.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/include_quoted_c.sv") + ); + } // }}} + + #[test] + fn include_quoted_d() { // {{{ + let (ret, _) = preprocess_usualargs("include_quoted_d.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/include_quoted_d.sv") + ); + } // }}} + + #[test] + fn include_recursive() { // {{{ + let ret = preprocess_usualargs("include_recursive.svh"); + let expected = format!( + "Err({}ExceedRecursiveLimit{})", + "Include { source: ".repeat(RECURSIVE_LIMIT+1), + " }".repeat(RECURSIVE_LIMIT+1), + ); + assert_eq!(format!("{:?}", ret), expected); + } // }}} + + #[test] + fn include_sameline_comment() { // {{{ + let (ret, _) = preprocess_usualargs("include_sameline_comment.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/include_sameline_comment.sv") ); assert_eq!( ret.origin(10).unwrap().0, - &PathBuf::from(get_testcase("test2.sv")) + &PathBuf::from(testfile_path("include_sameline_comment.sv")) ); assert_eq!(ret.origin(10).unwrap().1, 10); assert_eq!( ret.origin(50).unwrap().0, - &PathBuf::from(get_testcase("test2.svh")) + &PathBuf::from(testfile_path("included.svh")) ); assert_eq!(ret.origin(50).unwrap().1, 73); assert_eq!( ret.origin(70).unwrap().0, - &PathBuf::from(get_testcase("test2.sv")) + &PathBuf::from(testfile_path("include_sameline_comment.sv")) ); - assert_eq!(ret.origin(70).unwrap().1, 50); - } + assert_eq!(ret.origin(70).unwrap().1, 53); + } // }}} #[test] - fn test2_ignore_include() { - let include_paths = [get_testcase("")]; - let (ret, _) = preprocess( - get_testcase("test2.sv"), - &HashMap::new(), - &include_paths, - false, - true, - ) - .unwrap(); - assert_eq!( - ret.text(), - r##"module and_op (a, b, c); - -endmodule -"## - ); - } - - #[test] - fn test3() { - let (ret, _) = preprocess( - get_testcase("test3.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); - assert_eq!( - ret.text(), - r##"`define connect(NAME, INDEX = 0) \ - assign NAME``_``INDEX``__x = NAME[INDEX].x; \ - assign NAME``_``INDEX``__y = NAME[INDEX].y; - -module a (); - - assign a_0__x = a[0].x; - assign a_0__y = a[0].y; assign a_1__x = a[1].x; - assign a_1__y = a[1].y; endmodule -"## - ); - } - - #[test] - fn test4() { - let (ret, _) = preprocess( - get_testcase("test4.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); - assert_eq!( - ret.text(), - r##"`define disp(clk, exp, msg) \ - always @(posedge clk) begin \ - if (!(exp)) begin \ - $display msg; \ - end \ - end \ - -module a (); - -always @(posedge clk) begin - if (!(!(a[i].b && c[i]))) begin - $display ("xxx(()[]]{}}}", a[i].b, c[i]); - end - end - ; - -endmodule -"## - ); - } - - #[test] - fn test5() { - let (ret, _) = preprocess( - get_testcase("test5.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); - assert_eq!( - ret.text(), - r##"module a; -`define HI Hello -`define LO "`HI, world" -`define H(x) "Hello, x" -initial begin -$display("`HI, world"); -$display("`HI, world" ); -$display("Hello, x" ); -end -endmodule -"## - ); - } - - #[test] - fn test6() { - let (ret, _) = preprocess( - get_testcase("test6.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); - assert_eq!( - ret.text(), - r##"`define msg(x,y) `"x: `\`"y`\`"`" - -module a; -initial begin -$display("left side: \"right side\"" ); -end -endmodule -"## - ); - } - - #[test] - fn test7() { - let ret = preprocess( - get_testcase("test7.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ); - assert_eq!(format!("{:?}", ret), "Err(ExceedRecursiveLimit)"); - } - - #[test] - fn test8() { - let ret = preprocess( - get_testcase("test8.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ); - assert_eq!(format!("{:?}", ret), "Err(ExceedRecursiveLimit)"); - } - - #[test] - fn test9() { - let include_paths = [get_testcase("")]; - let ret = preprocess( - get_testcase("test9.sv"), - &HashMap::new(), - &include_paths, - false, - false, - ); + fn include_sameline_include() { // {{{ + let ret = preprocess_usualargs("include_sameline_include.sv"); assert_eq!(format!("{:?}", ret), "Err(IncludeLine)"); - } + } // }}} #[test] - fn test10() { - let include_paths = [get_testcase("")]; - let ret = preprocess( - get_testcase("test10.sv"), - &HashMap::new(), - &include_paths, - false, - false, - ); + fn include_sameline_keyword() { // {{{ + let ret = preprocess_usualargs("include_sameline_keyword.sv"); assert_eq!(format!("{:?}", ret), "Err(IncludeLine)"); - } + } // }}} #[test] - fn test11() { - let (ret, _) = preprocess( - get_testcase("test11.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); + fn include_withindent() { // {{{ + let (ret, _) = preprocess_usualargs("include_withindent.sv").unwrap(); assert_eq!( ret.text(), - r##"module a; -initial begin - if (3 == 0) begin - end -end -endmodule -"## + testfile_contents("expected/include_withindent.sv") ); - } + + // 11th char of returned text is '_' in the module identifier + // "and_op", and originates from the parent file. + // Characters are zero-indexed. + let n = 10; + assert_eq!( + ret.origin(n).unwrap(), + (&PathBuf::from(testfile_path("include_withindent.sv")), n) + ); + assert_eq!(ret.text().chars().nth(n).unwrap(), '_'); + + // 59th char of returned text is 'n' in the primitive identifier + // "and", and originates from the child file at character index 72. + let n = 58; + assert_eq!( + ret.origin(n).unwrap(), + (&PathBuf::from(testfile_path("included.svh")), 72) + ); + assert_eq!(ret.text().chars().nth(n).unwrap(), 'n'); + + // 80th char of returned text is 'o' in the keyword "endmodule", and + // originates from the parent file. + let n = 79; + assert_eq!( + ret.origin(n).unwrap(), + (&PathBuf::from(testfile_path("include_withindent.sv")), 62) + ); + assert_eq!(ret.text().chars().nth(n).unwrap(), 'o'); + } // }}} #[test] - fn test12() { - let (ret, _) = preprocess( - get_testcase("test12.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); + fn keywords() { // {{{ + let (ret, _) = preprocess_usualargs("keywords.sv").unwrap(); assert_eq!( ret.text(), - r##"module a; -reg \`~!-_=+\|[]{};:'"",./<>? ; -endmodule -"## + testfile_contents("keywords.sv") ); - } + } // }}} #[test] - fn test13() { - let (ret, _) = preprocess( - get_testcase("test13.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); + fn line() { // {{{ + let (ret, _) = preprocess_usualargs("line.sv").unwrap(); assert_eq!( ret.text(), - r##"`define NAME 42 // Comment -interface foo #(WIDTH = 42 ) (); -endinterface -"## + testfile_contents("line.sv") ); - } + } // }}} #[test] - fn test14() { - let (ret, _) = preprocess( - get_testcase("test14.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); + fn macro_arguments() { // {{{ + let (ret, _) = preprocess_usualargs("macro_arguments.sv").unwrap(); assert_eq!( ret.text(), - r##"module A; -wire a = 1'b0; - - -endmodule -"## + testfile_contents("expected/macro_arguments.sv") ); - } + } // }}} #[test] - fn test15() { - let (ret, _) = preprocess( - get_testcase("test15.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); + fn macro_basic() { // {{{ + let (ret, _) = preprocess_usualargs("macro_basic.sv").unwrap(); assert_eq!( ret.text(), - r##"`define MOD_INST u_mysubmod -module mymod; -mysubmod u_mysubmod() ; -endmodule -"## + testfile_contents("expected/macro_basic.sv") ); - } + } // }}} #[test] - fn test16() { - let (ret, _) = preprocess( - get_testcase("test16.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); + fn macro_comment() { // {{{ + let (ret, _) = preprocess_usualargs("macro_comment.sv").unwrap(); assert_eq!( ret.text(), - r##"module a; -`define HELLO0 "HELLO" -`define \HELLO1 "HELLO" -initial begin -$display("HELLO" ); -$display("HELLO" ); -$display("HELLO" ); -$display("HELLO" ); -end -endmodule -"## + testfile_contents("expected/macro_comment.sv") ); - } + } // }}} #[test] - fn test17() { - let (ret, _) = preprocess( - get_testcase("test17.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); + fn macro_delimiters() { // {{{ + let (ret, _) = preprocess_usualargs("macro_delimiters.sv").unwrap(); assert_eq!( ret.text(), - r##"`define A \ - initial begin // comment \ - end - -module test(); - -initial begin - end endmodule -"## + testfile_contents("expected/macro_delimiters.sv") ); - } + } // }}} #[test] - fn test18() { - let (ret, _) = preprocess( - get_testcase("test18.sv"), - &HashMap::new(), - &[] as &[String], - false, - false, - ) - .unwrap(); + #[allow(non_snake_case)] + fn macro_FILE() { // {{{ + let (ret, _) = preprocess_usualargs("macro_FILE.sv").unwrap(); assert_eq!( ret.text(), - r##"// pragma translate_off -module A; -endmodule -// pragma translate_on - -"## + testfile_contents("expected/macro_FILE.sv") ); - } + } // }}} #[test] - fn test19() { - let include_paths = [get_testcase("")]; - let (ret, _) = preprocess( - get_testcase("test19.sv"), - &HashMap::new(), - &include_paths, - false, - false, - ) - .unwrap(); + fn macro_identifier() { // {{{ + let (ret, _) = preprocess_usualargs("macro_identifier.sv").unwrap(); assert_eq!( ret.text(), - r##"module and_op (a, b, c); -output a; -input b, c; - -and a1 (a,b,c); - - // comment -endmodule -"## + testfile_contents("expected/macro_identifier.sv") ); - assert_eq!( - ret.origin(10).unwrap().0, - &PathBuf::from(get_testcase("test19.sv")) - ); - assert_eq!(ret.origin(10).unwrap().1, 10); - assert_eq!( - ret.origin(50).unwrap().0, - &PathBuf::from(get_testcase("test2.svh")) - ); - assert_eq!(ret.origin(50).unwrap().1, 73); - assert_eq!( - ret.origin(70).unwrap().0, - &PathBuf::from(get_testcase("test19.sv")) - ); - assert_eq!(ret.origin(70).unwrap().1, 50); - } + } // }}} #[test] - fn test20() { - let include_paths = [get_testcase("")]; - let (ret, _) = preprocess( - get_testcase("test20.sv"), - &HashMap::new(), - &include_paths, - false, - false, - ) - .unwrap(); + #[allow(non_snake_case)] + fn macro_LINE() { // {{{ + let (ret, _) = preprocess_usualargs("macro_LINE.sv").unwrap(); assert_eq!( ret.text(), - r##"module and_op (a, b, c); - // a - output a; -input b, c; - -and a1 (a,b,c); - - -endmodule -"## + testfile_contents("expected/macro_LINE.sv") ); - assert_eq!( - ret.origin(10).unwrap().0, - &PathBuf::from(get_testcase("test20.sv")) - ); - assert_eq!(ret.origin(10).unwrap().1, 10); - assert_eq!( - ret.origin(60).unwrap().0, - &PathBuf::from(get_testcase("test2.svh")) - ); - assert_eq!(ret.origin(60).unwrap().1, 74); - assert_eq!( - ret.origin(80).unwrap().0, - &PathBuf::from(get_testcase("test20.sv")) - ); - assert_eq!(ret.origin(80).unwrap().1, 60); - } + } // }}} - // Check that preprocess() doesn't introduce extra whitespace within and - // around compiler directives. #[test] - fn test21() { - let include_paths = [get_testcase("")]; - let (ret, _) = preprocess( - get_testcase("test21.sv"), - &HashMap::new(), - &include_paths, - false, - false, - ) - .unwrap(); + fn macro_multiline_comment() { // {{{ + let (ret, _) = preprocess_usualargs("macro_multiline_comment.sv").unwrap(); assert_eq!( ret.text(), - r##"//top -`resetall -`timescale 10 us / 100 ns -`default_nettype wire -//first -`default_nettype none//middle -//last -`unconnected_drive pull0 -`unconnected_drive pull1 -`nounconnected_drive -`celldefine -`endcelldefine -`pragma foo -`pragma foo bar -`line 5 "foo" 0 -`begin_keywords "1800-2017" -`end_keywords -"## + testfile_contents("expected/macro_multiline_comment.sv") ); - } + } // }}} + + #[test] + fn macro_recursion_direct() { // {{{ + let ret = preprocess_usualargs("macro_recursion_direct.sv"); + assert_eq!(format!("{:?}", ret), "Err(ExceedRecursiveLimit)"); + } // }}} + + #[test] + fn macro_recursion_indirect() { // {{{ + let ret = preprocess_usualargs("macro_recursion_indirect.sv"); + assert_eq!(format!("{:?}", ret), "Err(ExceedRecursiveLimit)"); + } // }}} + + #[test] + fn pragma() { // {{{ + let (ret, _) = preprocess_usualargs("pragma.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("pragma.sv") + ); + } // }}} + + #[test] + fn resetall() { // {{{ + let (ret, _) = preprocess_usualargs("resetall.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("resetall.sv") + ); + } // }}} + + #[test] + fn timescale() { // {{{ + let (ret, _) = preprocess_usualargs("timescale.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("timescale.sv") + ); + } // }}} + + #[test] + fn unconnected_drive() { // {{{ + let (ret, _) = preprocess_usualargs("unconnected_drive.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("unconnected_drive.sv") + ); + } // }}} + + #[test] + fn undef() { // {{{ + let (ret, _) = preprocess_usualargs("undef.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/undef.sv") + ); + } // }}} + + #[test] + fn undefineall() { // {{{ + let (ret, _) = preprocess_usualargs("undefineall.sv").unwrap(); + assert_eq!( + ret.text(), + testfile_contents("expected/undefineall.sv") + ); + } // }}} } diff --git a/sv-parser-pp/testcases/IEEE18002017_keywords_if2_13642005.sv b/sv-parser-pp/testcases/IEEE18002017_keywords_if2_13642005.sv new file mode 100644 index 0000000..3caed7e --- /dev/null +++ b/sv-parser-pp/testcases/IEEE18002017_keywords_if2_13642005.sv @@ -0,0 +1,8 @@ + +`begin_keywords "1364-2005" // use IEEE Std 1364-2005 Verilog keywords +interface if2 (); // ERROR: "interface" is not a keyword in 1364-2005 + // This interface should pass the preprocessor, but not the main parser + // because the identifiers `interface` and `endinterface` are not reserved + // keywords in IEEE1364-2005. +endinterface // ERROR: "endinterface" is not a keyword in 1364-2005 +`end_keywords diff --git a/sv-parser-pp/testcases/IEEE18002017_keywords_m2_13642001.sv b/sv-parser-pp/testcases/IEEE18002017_keywords_m2_13642001.sv new file mode 100644 index 0000000..da6af52 --- /dev/null +++ b/sv-parser-pp/testcases/IEEE18002017_keywords_m2_13642001.sv @@ -0,0 +1,8 @@ + +`begin_keywords "1364-2001" +module m2 (); + // "logic" is NOT a reserved keyword in IEEE1364-2001. + // This module should pass both the preprocessor, AND the main parser. + reg [63:0] logic; +endmodule +`end_keywords diff --git a/sv-parser-pp/testcases/IEEE18002017_keywords_m2_18002005.sv b/sv-parser-pp/testcases/IEEE18002017_keywords_m2_18002005.sv new file mode 100644 index 0000000..f201bf1 --- /dev/null +++ b/sv-parser-pp/testcases/IEEE18002017_keywords_m2_18002005.sv @@ -0,0 +1,8 @@ + +`begin_keywords "1800-2005" +module m2 (); + // "logic" IS a reserved keyword in IEEE1800-2005. + // This module should pass both the preprocessor, but NOT the main parser. + reg [63:0] logic; +endmodule +`end_keywords diff --git a/sv-parser-pp/testcases/IEEE18002017_macro_argument_expansion.sv b/sv-parser-pp/testcases/IEEE18002017_macro_argument_expansion.sv new file mode 100644 index 0000000..9c1f62c --- /dev/null +++ b/sv-parser-pp/testcases/IEEE18002017_macro_argument_expansion.sv @@ -0,0 +1,10 @@ +/* IEEE1800-2017 Clause 22.5.1 page 679 +*/ + +`define max(a,b)((a) > (b) ? (a) : (b)) +`define TOP(a,b) a + b + +module m; +assign n = `max(p+q, r+s) ; +assign z = `TOP( `TOP(b,1), `TOP(42,a) ); +endmodule diff --git a/sv-parser-pp/testcases/IEEE18002017_macro_delimit_tokens.sv b/sv-parser-pp/testcases/IEEE18002017_macro_delimit_tokens.sv new file mode 100644 index 0000000..9c60efb --- /dev/null +++ b/sv-parser-pp/testcases/IEEE18002017_macro_delimit_tokens.sv @@ -0,0 +1,7 @@ +/* IEEE1800-2017 Clause 22.5.1 page 680 +*/ + +`define append(f) f``_master + +module `append(clock); +endmodule diff --git a/sv-parser-pp/testcases/test6.sv b/sv-parser-pp/testcases/IEEE18002017_macro_mix_quotes.sv similarity index 71% rename from sv-parser-pp/testcases/test6.sv rename to sv-parser-pp/testcases/IEEE18002017_macro_mix_quotes.sv index 752c735..0f6e6a0 100644 --- a/sv-parser-pp/testcases/test6.sv +++ b/sv-parser-pp/testcases/IEEE18002017_macro_mix_quotes.sv @@ -1,3 +1,6 @@ +/* IEEE1800-2017 Clause 22.5.1 page 680 +*/ + `define msg(x,y) `"x: `\`"y`\`"`" module a; diff --git a/sv-parser-pp/testcases/IEEE18002017_macro_noexpand_string.sv b/sv-parser-pp/testcases/IEEE18002017_macro_noexpand_string.sv new file mode 100644 index 0000000..bcc6ee3 --- /dev/null +++ b/sv-parser-pp/testcases/IEEE18002017_macro_noexpand_string.sv @@ -0,0 +1,13 @@ +/* IEEE1800-2017 Clause 22.5.1 page 679 +*/ + +module main; +`define HI Hello +`define LO "`HI, world" +`define H(x) "Hello, x" +initial begin + $display("`HI, world"); + $display(`LO); + $display(`H(world)); +end +endmodule diff --git a/sv-parser-pp/testcases/IEEE18002017_macro_with_defaults.sv b/sv-parser-pp/testcases/IEEE18002017_macro_with_defaults.sv new file mode 100644 index 0000000..2a9b2a3 --- /dev/null +++ b/sv-parser-pp/testcases/IEEE18002017_macro_with_defaults.sv @@ -0,0 +1,25 @@ +/* IEEE1800-2017 Clause 22.5.1 page 678 +* NOTE: Illegal cases are not included in this testcase. +* NOTE: Use of EMPTY is suggested on page 679 +*/ + +`define MACRO1(a=5,b="B",c) $display(a,,b,,c); +`define MACRO2(a=5, b, c="C") $display(a,,b,,c); +`define MACRO3(a=5, b=0, c="C") $display(a,,b,,c); + +`define EMPTY + +module m; +initial begin + `MACRO1 ( , 2, 3 ) + `MACRO1 ( 1 , , 3 ) + `MACRO1 ( , 2, ) + `MACRO2 (1, , 3) + `MACRO2 (, 2, ) + `MACRO2 (, 2) + `MACRO3 ( 1 ) + `MACRO3 ( ) + + `MACRO3 (`EMPTY,`EMPTY,`EMPTY) +end +endmodule diff --git a/sv-parser-pp/testcases/IEEE18002017_macro_without_defaults.sv b/sv-parser-pp/testcases/IEEE18002017_macro_without_defaults.sv new file mode 100644 index 0000000..bf21d4a --- /dev/null +++ b/sv-parser-pp/testcases/IEEE18002017_macro_without_defaults.sv @@ -0,0 +1,13 @@ +/* IEEE1800-2017 Clause 22.5.1 page 677 +* NOTE: Illegal cases are not included in this testcase. +*/ + +`define D(x,y) initial $display("start", x , y, "end"); + +module m; + `D( "msg1" , "msg2" ) + `D( " msg1", ) + `D(, "msg2 ") + `D(,) + `D( , ) +endmodule diff --git a/sv-parser-pp/testcases/celldefine.sv b/sv-parser-pp/testcases/celldefine.sv new file mode 100644 index 0000000..779f8f7 --- /dev/null +++ b/sv-parser-pp/testcases/celldefine.sv @@ -0,0 +1,13 @@ +// IEEE1800-2017 Clause 22.10 +// The directives `celldefine and `endcelldefine tag modules as cell modules. +// Cells are used by certain PLI routines and may be useful for applications +// such as delay calculations. It is advisable to pair each `celldefine with an +// `endcelldefine, but it is not required. The latest occurrence of either +// directive in the source controls whether modules are tagged as cell modules. +// More than one of these pairs may appear in a single source description. +// These directives may appear anywhere in the source description, but it is +// recommended that the directives be specified outside any design elements. +// The `resetall directive includes the effects of a `endcelldefine directive. +`celldefine +`endcelldefine +// This file should be emitted from the preprocessor unchanged. diff --git a/sv-parser-pp/testcases/default_nettype.sv b/sv-parser-pp/testcases/default_nettype.sv new file mode 100644 index 0000000..11b23f0 --- /dev/null +++ b/sv-parser-pp/testcases/default_nettype.sv @@ -0,0 +1,22 @@ +// IEEE1800-2017 Clause 22.8 +// The directive `default_nettype controls the net type created for implicit +// net declarations. It can be used only outside design elements. Multiple +// `default_nettype directives are allowed. The latest occurrence of this +// directive in the source controls the type of nets that will be implicitly +// declared. +// When no `default_nettype directive is present or if the `resetall directive +// is specified, implicit nets are of type wire. When the `default_nettype is +// set to none, all nets shall be explicitly declared. If a net is not +// explicitly declared, an error is generated. +`default_nettype wire // Comment immmediately after keyword+space +`default_nettype tri +`default_nettype tri0 +`default_nettype tri1 +`default_nettype wand +`default_nettype triand +`default_nettype wor +`default_nettype trior +`default_nettype trireg +`default_nettype uwire +`default_nettype none// Comment immmediately after keyword +// This file should be emitted from the preprocessor unchanged. diff --git a/sv-parser-pp/testcases/test12.sv b/sv-parser-pp/testcases/escaped_identifier.sv similarity index 100% rename from sv-parser-pp/testcases/test12.sv rename to sv-parser-pp/testcases/escaped_identifier.sv diff --git a/sv-parser-pp/testcases/expected/IEEE18002017_keywords_if2_13642005.sv b/sv-parser-pp/testcases/expected/IEEE18002017_keywords_if2_13642005.sv new file mode 100644 index 0000000..3caed7e --- /dev/null +++ b/sv-parser-pp/testcases/expected/IEEE18002017_keywords_if2_13642005.sv @@ -0,0 +1,8 @@ + +`begin_keywords "1364-2005" // use IEEE Std 1364-2005 Verilog keywords +interface if2 (); // ERROR: "interface" is not a keyword in 1364-2005 + // This interface should pass the preprocessor, but not the main parser + // because the identifiers `interface` and `endinterface` are not reserved + // keywords in IEEE1364-2005. +endinterface // ERROR: "endinterface" is not a keyword in 1364-2005 +`end_keywords diff --git a/sv-parser-pp/testcases/expected/IEEE18002017_keywords_m2_13642001.sv b/sv-parser-pp/testcases/expected/IEEE18002017_keywords_m2_13642001.sv new file mode 100644 index 0000000..da6af52 --- /dev/null +++ b/sv-parser-pp/testcases/expected/IEEE18002017_keywords_m2_13642001.sv @@ -0,0 +1,8 @@ + +`begin_keywords "1364-2001" +module m2 (); + // "logic" is NOT a reserved keyword in IEEE1364-2001. + // This module should pass both the preprocessor, AND the main parser. + reg [63:0] logic; +endmodule +`end_keywords diff --git a/sv-parser-pp/testcases/expected/IEEE18002017_keywords_m2_18002005.sv b/sv-parser-pp/testcases/expected/IEEE18002017_keywords_m2_18002005.sv new file mode 100644 index 0000000..f201bf1 --- /dev/null +++ b/sv-parser-pp/testcases/expected/IEEE18002017_keywords_m2_18002005.sv @@ -0,0 +1,8 @@ + +`begin_keywords "1800-2005" +module m2 (); + // "logic" IS a reserved keyword in IEEE1800-2005. + // This module should pass both the preprocessor, but NOT the main parser. + reg [63:0] logic; +endmodule +`end_keywords diff --git a/sv-parser-pp/testcases/expected/IEEE18002017_macro_argument_expansion.sv b/sv-parser-pp/testcases/expected/IEEE18002017_macro_argument_expansion.sv new file mode 100644 index 0000000..2688806 --- /dev/null +++ b/sv-parser-pp/testcases/expected/IEEE18002017_macro_argument_expansion.sv @@ -0,0 +1,10 @@ +/* IEEE1800-2017 Clause 22.5.1 page 679 +*/ + +`define max(a,b)((a) > (b) ? (a) : (b)) +`define TOP(a,b) a + b + +module m; +assign n = ((p+q) > (r+s) ? (p+q) : (r+s)) ; +assign z = b + 1 + 42 + a; +endmodule diff --git a/sv-parser-pp/testcases/expected/IEEE18002017_macro_delimit_tokens.sv b/sv-parser-pp/testcases/expected/IEEE18002017_macro_delimit_tokens.sv new file mode 100644 index 0000000..abdd98c --- /dev/null +++ b/sv-parser-pp/testcases/expected/IEEE18002017_macro_delimit_tokens.sv @@ -0,0 +1,7 @@ +/* IEEE1800-2017 Clause 22.5.1 page 680 +*/ + +`define append(f) f``_master + +module clock_master; +endmodule diff --git a/sv-parser-pp/testcases/expected/IEEE18002017_macro_mix_quotes.sv b/sv-parser-pp/testcases/expected/IEEE18002017_macro_mix_quotes.sv new file mode 100644 index 0000000..6c7dd6d --- /dev/null +++ b/sv-parser-pp/testcases/expected/IEEE18002017_macro_mix_quotes.sv @@ -0,0 +1,10 @@ +/* IEEE1800-2017 Clause 22.5.1 page 680 +*/ + +`define msg(x,y) `"x: `\`"y`\`"`" + +module a; +initial begin +$display("left side: \"right side\""); +end +endmodule diff --git a/sv-parser-pp/testcases/expected/IEEE18002017_macro_noexpand_string.sv b/sv-parser-pp/testcases/expected/IEEE18002017_macro_noexpand_string.sv new file mode 100644 index 0000000..27ec41d --- /dev/null +++ b/sv-parser-pp/testcases/expected/IEEE18002017_macro_noexpand_string.sv @@ -0,0 +1,13 @@ +/* IEEE1800-2017 Clause 22.5.1 page 679 +*/ + +module main; +`define HI Hello +`define LO "`HI, world" +`define H(x) "Hello, x" +initial begin + $display("`HI, world"); + $display("`HI, world"); + $display("Hello, x"); +end +endmodule diff --git a/sv-parser-pp/testcases/expected/IEEE18002017_macro_with_defaults.sv b/sv-parser-pp/testcases/expected/IEEE18002017_macro_with_defaults.sv new file mode 100644 index 0000000..3090454 --- /dev/null +++ b/sv-parser-pp/testcases/expected/IEEE18002017_macro_with_defaults.sv @@ -0,0 +1,25 @@ +/* IEEE1800-2017 Clause 22.5.1 page 678 +* NOTE: Illegal cases are not included in this testcase. +* NOTE: Use of EMPTY is suggested on page 679 +*/ + +`define MACRO1(a=5,b="B",c) $display(a,,b,,c); +`define MACRO2(a=5, b, c="C") $display(a,,b,,c); +`define MACRO3(a=5, b=0, c="C") $display(a,,b,,c); + +`define EMPTY + +module m; +initial begin + $display(5,,2,,3); + $display(1,,"B",,3); + $display(5,,2,,); + $display(1,,,,3); + $display(5,,2,,"C"); + $display(5,,2,,"C"); + $display(1,,0,,"C"); + $display(5,,0,,"C"); + + $display(,,,,); +end +endmodule diff --git a/sv-parser-pp/testcases/expected/IEEE18002017_macro_without_defaults.sv b/sv-parser-pp/testcases/expected/IEEE18002017_macro_without_defaults.sv new file mode 100644 index 0000000..489a121 --- /dev/null +++ b/sv-parser-pp/testcases/expected/IEEE18002017_macro_without_defaults.sv @@ -0,0 +1,13 @@ +/* IEEE1800-2017 Clause 22.5.1 page 677 +* NOTE: Illegal cases are not included in this testcase. +*/ + +`define D(x,y) initial $display("start", x , y, "end"); + +module m; + initial $display("start", "msg1" , "msg2", "end"); + initial $display("start", " msg1" , , "end"); + initial $display("start", , "msg2 ", "end"); + initial $display("start", , , "end"); + initial $display("start", , , "end"); +endmodule diff --git a/sv-parser-pp/testcases/expected/escaped_identifier.sv b/sv-parser-pp/testcases/expected/escaped_identifier.sv new file mode 100644 index 0000000..871f152 --- /dev/null +++ b/sv-parser-pp/testcases/expected/escaped_identifier.sv @@ -0,0 +1,3 @@ +module a; +reg \`~!-_=+\|[]{};:'"",./<>? ; +endmodule diff --git a/sv-parser-pp/testcases/expected/ifdef_nested.sv b/sv-parser-pp/testcases/expected/ifdef_nested.sv new file mode 100644 index 0000000..3e0aa16 --- /dev/null +++ b/sv-parser-pp/testcases/expected/ifdef_nested.sv @@ -0,0 +1,5 @@ +module A; +wire a = 1'b0; + + +endmodule diff --git a/sv-parser-pp/testcases/expected/ifdef_predefined.sv b/sv-parser-pp/testcases/expected/ifdef_predefined.sv new file mode 100644 index 0000000..449fefb --- /dev/null +++ b/sv-parser-pp/testcases/expected/ifdef_predefined.sv @@ -0,0 +1,7 @@ +module and_op (a, b, c); +output a; +input b, c; + +wire a = b & c; + +endmodule diff --git a/sv-parser-pp/testcases/expected/ifdef_undefined.sv b/sv-parser-pp/testcases/expected/ifdef_undefined.sv new file mode 100644 index 0000000..deed77b --- /dev/null +++ b/sv-parser-pp/testcases/expected/ifdef_undefined.sv @@ -0,0 +1,7 @@ +module and_op (a, b, c); +output a; +input b, c; + +and a1 (a,b,c); + +endmodule diff --git a/sv-parser-pp/testcases/expected/ifndef_undefined.sv b/sv-parser-pp/testcases/expected/ifndef_undefined.sv new file mode 100644 index 0000000..a87d075 --- /dev/null +++ b/sv-parser-pp/testcases/expected/ifndef_undefined.sv @@ -0,0 +1,5 @@ +// pragma translate_off +module A; +endmodule +// pragma translate_on + diff --git a/sv-parser-pp/testcases/test2.sv b/sv-parser-pp/testcases/expected/include_ignore.sv similarity index 62% rename from sv-parser-pp/testcases/test2.sv rename to sv-parser-pp/testcases/expected/include_ignore.sv index 873c201..d35eb08 100644 --- a/sv-parser-pp/testcases/test2.sv +++ b/sv-parser-pp/testcases/expected/include_ignore.sv @@ -1,3 +1,3 @@ module and_op (a, b, c); -`include "test2.svh" + endmodule diff --git a/sv-parser-pp/testcases/expected/include_noindent.sv b/sv-parser-pp/testcases/expected/include_noindent.sv new file mode 100644 index 0000000..9707d04 --- /dev/null +++ b/sv-parser-pp/testcases/expected/include_noindent.sv @@ -0,0 +1,8 @@ +module and_op (a, b, c); +output a; +input b, c; + +and a1 (a,b,c); + + +endmodule diff --git a/sv-parser-pp/testcases/expected/include_quoted_c.sv b/sv-parser-pp/testcases/expected/include_quoted_c.sv new file mode 100644 index 0000000..b663ef9 --- /dev/null +++ b/sv-parser-pp/testcases/expected/include_quoted_c.sv @@ -0,0 +1,9 @@ +// Based on last example of IEEE1800-2017 Clause 22.5.1, page 680. +`define APPEND_SVH(path) `"path.svh`" +module and_op (a, b, c); +output a; +input b, c; + +and a1 (a,b,c); + +endmodule diff --git a/sv-parser-pp/testcases/expected/include_quoted_d.sv b/sv-parser-pp/testcases/expected/include_quoted_d.sv new file mode 100644 index 0000000..68a6a95 --- /dev/null +++ b/sv-parser-pp/testcases/expected/include_quoted_d.sv @@ -0,0 +1,8 @@ +`define PATH "included.svh" +module and_op (a, b, c); +output a; +input b, c; + +and a1 (a,b,c); + +endmodule diff --git a/sv-parser-pp/testcases/expected/include_sameline_comment.sv b/sv-parser-pp/testcases/expected/include_sameline_comment.sv new file mode 100644 index 0000000..3f7265d --- /dev/null +++ b/sv-parser-pp/testcases/expected/include_sameline_comment.sv @@ -0,0 +1,8 @@ +module and_op (a, b, c); +output a; +input b, c; + +and a1 (a,b,c); + + // comment +endmodule diff --git a/sv-parser-pp/testcases/expected/include_withindent.sv b/sv-parser-pp/testcases/expected/include_withindent.sv new file mode 100644 index 0000000..3549925 --- /dev/null +++ b/sv-parser-pp/testcases/expected/include_withindent.sv @@ -0,0 +1,9 @@ +module and_op (a, b, c); + // a + output a; +input b, c; + +and a1 (a,b,c); + + +endmodule diff --git a/sv-parser-pp/testcases/expected/macro_FILE.sv b/sv-parser-pp/testcases/expected/macro_FILE.sv new file mode 100644 index 0000000..6edf5c2 --- /dev/null +++ b/sv-parser-pp/testcases/expected/macro_FILE.sv @@ -0,0 +1,15 @@ +// __FILE__ = `__FILE__ + +// This block SHOULD be emitted from the preprocessor. + + +// Emitted instead. + + +// The following define should have no effect. +`define __FILE__ "FOO" + +// The following undef should have no effect. +`undef __FILE__ + +// NOTE: Comparison against expected value are destined to fail in testcase. diff --git a/sv-parser-pp/testcases/expected/macro_LINE.sv b/sv-parser-pp/testcases/expected/macro_LINE.sv new file mode 100644 index 0000000..58ec595 --- /dev/null +++ b/sv-parser-pp/testcases/expected/macro_LINE.sv @@ -0,0 +1,21 @@ +// __LINE__ = `__LINE__ + +// This block SHOULD be emitted from the preprocessor. + + +// Emitted instead. + + +// The following define should have no effect. +`define __LINE__ -2 + +// The following undef should have no effect. +`undef __LINE__ + +module M; + initial + if (26 == 28) // Should be "26 == 28". + $display("PASS"); + else if (28 == 28) // Should be "28 == 28". + $display("FAIL"); +endmodule diff --git a/sv-parser-pp/testcases/expected/macro_arguments.sv b/sv-parser-pp/testcases/expected/macro_arguments.sv new file mode 100644 index 0000000..0637660 --- /dev/null +++ b/sv-parser-pp/testcases/expected/macro_arguments.sv @@ -0,0 +1,23 @@ +// Macro with parameters with usage spread over multiple lines. +// Final line of macro is line14. +// Argument value `clk` is equal to its name. +// Argument value of exp contains matching brackets and parentheses. +// Bracketed value of msg is required to avoid being parsed as a parameterized +// macro instead of argumnts to $display. +// NOTE: Trailing whitespace is not exercised here, i.e. continuations +// immediately follow non-whitespace. +`define disp(clk, exp, msg)\ + always @(posedge clk)\ + if (exp) begin\ + $display msg;\ + end\ + +module M (); + + always @(posedge clk) + if (!(a[i].b && c[i])) begin + $display ("xxx(()[]]{}}}", a[i].b, c[i]); + end +; // NOTE: Semi-colon is unnecessary. + +endmodule diff --git a/sv-parser-pp/testcases/expected/macro_basic.sv b/sv-parser-pp/testcases/expected/macro_basic.sv new file mode 100644 index 0000000..6ff2696 --- /dev/null +++ b/sv-parser-pp/testcases/expected/macro_basic.sv @@ -0,0 +1,6 @@ +`define A aaa +module M; +aaa#() a0 (.*); // No trailing whitespace. +aaa #() a1 (.*); // Trailing 1 space. +aaa #() a2 (.*); // Trailing 2 spaces. +endmodule diff --git a/sv-parser-pp/testcases/expected/macro_comment.sv b/sv-parser-pp/testcases/expected/macro_comment.sv new file mode 100644 index 0000000..a9759dd --- /dev/null +++ b/sv-parser-pp/testcases/expected/macro_comment.sv @@ -0,0 +1,16 @@ +/* IEEE1800-2017 Clause 22.5.1, page 676 +If a one-line comment (that is, a comment specified with the characters //) is +included in the text, then the comment shall not become part of the substituted +text. +*/ + +// A has no comment +// B has a comment after 1 space +// C has a comment after 3 spaces +`define A 11 +`define B 22 // Comment not included in macro, but whitespace before `//` is. +`define C 33 // Comment not included in macro, but whitespace before `//` is. + +interface A #(p=11) (); endinterface +interface B #(p=22 ) (); endinterface +interface C #(p=33 ) (); endinterface diff --git a/sv-parser-pp/testcases/expected/macro_delimiters.sv b/sv-parser-pp/testcases/expected/macro_delimiters.sv new file mode 100644 index 0000000..d3abef6 --- /dev/null +++ b/sv-parser-pp/testcases/expected/macro_delimiters.sv @@ -0,0 +1,17 @@ +// Multi-line macro defined with 2 trailing spaces before initial continuation. +// First line has no trailing space, second has trailing space, third is end. +// Macro contains 2-space indent, so indented usage gets extra. +// Delimiters (``) used before and after arguments. +`define connect(NAME, INDEX = 0) \ + assign NAME``_``INDEX``__x = NAME[INDEX].x;\ + assign NAME``_``INDEX``__y = NAME[INDEX].y; \ + assign NAME``_``INDEX``__z = NAME[INDEX].z; + +module M (); + assign a_0__x = a[0].x; + assign a_0__y = a[0].y; + assign a_0__z = a[0].z; + assign a_1__x = a[1].x; + assign a_1__y = a[1].y; + assign a_1__z = a[1].z; +endmodule diff --git a/sv-parser-pp/testcases/expected/macro_identifier.sv b/sv-parser-pp/testcases/expected/macro_identifier.sv new file mode 100644 index 0000000..3812a08 --- /dev/null +++ b/sv-parser-pp/testcases/expected/macro_identifier.sv @@ -0,0 +1,10 @@ +`define A "aaa" +`define \B "bbb" +module M; + initial begin + $display("aaa"); + $display("aaa" ); + $display("bbb"); + $display("bbb" ); + end +endmodule diff --git a/sv-parser-pp/testcases/expected/macro_multiline_comment.sv b/sv-parser-pp/testcases/expected/macro_multiline_comment.sv new file mode 100644 index 0000000..989e21b --- /dev/null +++ b/sv-parser-pp/testcases/expected/macro_multiline_comment.sv @@ -0,0 +1,19 @@ +// Leading whitespace on 4 lines. +// initial \ Space before line continuation. +// begin\ No space before line continuation. +// $display(); // comment \ Continuation at end of comment. +// end +// NOTE: Trailing whitespace on lines ending `initial ` and `$display(); `. + +`define A \ + initial \ + begin\ + $display(); // comment \ + end + +module M; + initial + begin + $display(); + end +endmodule diff --git a/sv-parser-pp/testcases/expected/undef.sv b/sv-parser-pp/testcases/expected/undef.sv new file mode 100644 index 0000000..e0e4eff --- /dev/null +++ b/sv-parser-pp/testcases/expected/undef.sv @@ -0,0 +1,20 @@ +// IEEE1800-2017 Clause 22.5.2 +// The directive `undef shall undefine the specified text macro if previously +// defined by a `define compiler directive within the compilation unit. An +// attempt to undefine a text macro that was not previously defined using a +// `define compiler directive can issue a warning. +`undef FOO +`undef FOO// Comment +`undef FOO // Comment + +`define FOO foo +// AAA +// This block SHOULD be emitted from the preprocessor. + + + +`undef FOO + +// BBB +// This block SHOULD be emitted from the preprocessor. + diff --git a/sv-parser-pp/testcases/expected/undefineall.sv b/sv-parser-pp/testcases/expected/undefineall.sv new file mode 100644 index 0000000..9e5be19 --- /dev/null +++ b/sv-parser-pp/testcases/expected/undefineall.sv @@ -0,0 +1,29 @@ +// IEEE1800-2017 Clause 22.5.3 +// The directive `undefineall directive shall undefine all text macros +// previously defined by `define compiler directives within the compilation +// unit. This directive takes no argument and may appear anywhere in the source +// description. +`undefineall +`undefineall// Comment +`undefineall // Comment + +`define FOO foo +`define BAR bar +// AAA +// This block SHOULD be emitted from the preprocessor. + + +// BBB +// This block SHOULD be emitted from the preprocessor. + + + +`undefineall + +// CCC +// This block SHOULD be emitted from the preprocessor. + + +// DDD +// This block SHOULD be emitted from the preprocessor. + diff --git a/sv-parser-pp/testcases/test14.sv b/sv-parser-pp/testcases/ifdef_nested.sv similarity index 100% rename from sv-parser-pp/testcases/test14.sv rename to sv-parser-pp/testcases/ifdef_nested.sv diff --git a/sv-parser-pp/testcases/test1.sv b/sv-parser-pp/testcases/ifdef_predefined.sv similarity index 100% rename from sv-parser-pp/testcases/test1.sv rename to sv-parser-pp/testcases/ifdef_predefined.sv diff --git a/sv-parser-pp/testcases/ifdef_undefined.sv b/sv-parser-pp/testcases/ifdef_undefined.sv new file mode 100644 index 0000000..51567d5 --- /dev/null +++ b/sv-parser-pp/testcases/ifdef_undefined.sv @@ -0,0 +1,10 @@ +module and_op (a, b, c); +output a; +input b, c; + +`ifdef behavioral + wire a = b & c; +`else + and a1 (a,b,c); +`endif +endmodule diff --git a/sv-parser-pp/testcases/test18.sv b/sv-parser-pp/testcases/ifndef_undefined.sv similarity index 100% rename from sv-parser-pp/testcases/test18.sv rename to sv-parser-pp/testcases/ifndef_undefined.sv diff --git a/sv-parser-pp/testcases/test19.sv b/sv-parser-pp/testcases/include_ignore.sv similarity index 52% rename from sv-parser-pp/testcases/test19.sv rename to sv-parser-pp/testcases/include_ignore.sv index bb10a1f..1c757dd 100644 --- a/sv-parser-pp/testcases/test19.sv +++ b/sv-parser-pp/testcases/include_ignore.sv @@ -1,3 +1,3 @@ module and_op (a, b, c); -`include "test2.svh" // comment +`include "included.svh" endmodule diff --git a/sv-parser-pp/testcases/include_noindent.sv b/sv-parser-pp/testcases/include_noindent.sv new file mode 100644 index 0000000..1c757dd --- /dev/null +++ b/sv-parser-pp/testcases/include_noindent.sv @@ -0,0 +1,3 @@ +module and_op (a, b, c); +`include "included.svh" +endmodule diff --git a/sv-parser-pp/testcases/include_quoted_a.sv b/sv-parser-pp/testcases/include_quoted_a.sv new file mode 100644 index 0000000..8fd3b7b --- /dev/null +++ b/sv-parser-pp/testcases/include_quoted_a.sv @@ -0,0 +1,5 @@ +`define PATH included.svh +`define QUOTE(path) `"path`" +module and_op (a, b, c); +`include `QUOTE(`PATH) +endmodule diff --git a/sv-parser-pp/testcases/include_quoted_b.sv b/sv-parser-pp/testcases/include_quoted_b.sv new file mode 100644 index 0000000..e4168a0 --- /dev/null +++ b/sv-parser-pp/testcases/include_quoted_b.sv @@ -0,0 +1,5 @@ +`define PATH included.svh +`define QUOTED_PATH `"`PATH`" +module and_op (a, b, c); +`include `QUOTED_PATH +endmodule diff --git a/sv-parser-pp/testcases/include_quoted_c.sv b/sv-parser-pp/testcases/include_quoted_c.sv new file mode 100644 index 0000000..36393c4 --- /dev/null +++ b/sv-parser-pp/testcases/include_quoted_c.sv @@ -0,0 +1,5 @@ +// Based on last example of IEEE1800-2017 Clause 22.5.1, page 680. +`define APPEND_SVH(path) `"path.svh`" +module and_op (a, b, c); +`include `APPEND_SVH(included) +endmodule diff --git a/sv-parser-pp/testcases/include_quoted_d.sv b/sv-parser-pp/testcases/include_quoted_d.sv new file mode 100644 index 0000000..8e5eb97 --- /dev/null +++ b/sv-parser-pp/testcases/include_quoted_d.sv @@ -0,0 +1,4 @@ +`define PATH "included.svh" +module and_op (a, b, c); +`include `PATH +endmodule diff --git a/sv-parser-pp/testcases/include_recursive.svh b/sv-parser-pp/testcases/include_recursive.svh new file mode 100644 index 0000000..d84de3e --- /dev/null +++ b/sv-parser-pp/testcases/include_recursive.svh @@ -0,0 +1,3 @@ +// foo +`include "include_recursive.svh" +// bar diff --git a/sv-parser-pp/testcases/include_sameline_comment.sv b/sv-parser-pp/testcases/include_sameline_comment.sv new file mode 100644 index 0000000..1f17750 --- /dev/null +++ b/sv-parser-pp/testcases/include_sameline_comment.sv @@ -0,0 +1,3 @@ +module and_op (a, b, c); +`include "included.svh" // comment +endmodule diff --git a/sv-parser-pp/testcases/include_sameline_include.sv b/sv-parser-pp/testcases/include_sameline_include.sv new file mode 100644 index 0000000..64b02fb --- /dev/null +++ b/sv-parser-pp/testcases/include_sameline_include.sv @@ -0,0 +1,3 @@ +module and_op (a, b, c); +`include "included.svh" `include "included.svh" +endmodule diff --git a/sv-parser-pp/testcases/include_sameline_keyword.sv b/sv-parser-pp/testcases/include_sameline_keyword.sv new file mode 100644 index 0000000..acd0ae3 --- /dev/null +++ b/sv-parser-pp/testcases/include_sameline_keyword.sv @@ -0,0 +1,2 @@ +module and_op (a, b, c); +`include "included.svh" endmodule diff --git a/sv-parser-pp/testcases/test20.sv b/sv-parser-pp/testcases/include_withindent.sv similarity index 61% rename from sv-parser-pp/testcases/test20.sv rename to sv-parser-pp/testcases/include_withindent.sv index 63fe552..76fe96f 100644 --- a/sv-parser-pp/testcases/test20.sv +++ b/sv-parser-pp/testcases/include_withindent.sv @@ -1,4 +1,4 @@ module and_op (a, b, c); // a - `include "test2.svh" + `include "included.svh" endmodule diff --git a/sv-parser-pp/testcases/test2.svh b/sv-parser-pp/testcases/included.svh similarity index 100% rename from sv-parser-pp/testcases/test2.svh rename to sv-parser-pp/testcases/included.svh diff --git a/sv-parser-pp/testcases/keywords.sv b/sv-parser-pp/testcases/keywords.sv new file mode 100644 index 0000000..f053565 --- /dev/null +++ b/sv-parser-pp/testcases/keywords.sv @@ -0,0 +1,50 @@ +// IEEE1800-2017 Clause 22.14 +// A pair of directives, `begin_keywords and `end_keywords, can be used to +// specify what identifiers are reserved as keywords within a block of source +// code, based on a specific version of IEEE Std 1364 or IEEE Std 1800. +// The `begin_keywords and `end_keywords directives only specify the set of +// identifiers that are reserved as keywords. The directives do not affect the +// semantics, tokens, and other aspects of the SystemVerilog language. +// The `begin_keywords and `end_keywords directives can only be specified +// outside a design element. The `begin_keywords directive affects all source +// code that follows the directive, even across source code file boundaries, +// until the matching `end_keywords directive or the end of the compilation +// unit. The results of these directives are not affected by the `resetall +// directive. +`begin_keywords "1800-2017" +`end_keywords +`begin_keywords "1800-2012" +`end_keywords +`begin_keywords "1800-2009" +`end_keywords +`begin_keywords "1800-2005" +`end_keywords +`begin_keywords "1364-2005" +`end_keywords +`begin_keywords "1364-2001" +`end_keywords +`begin_keywords "1364-2001-noconfig" +`end_keywords +`begin_keywords "1364-1995" +`end_keywords +// The `begin_keywords `end_keywords directive pair can be nested. Each nested +// pair is stacked so that when an `end_keywords directive is encountered, the +// implementation returns to using the version_ specifier that was in effect +// prior to the matching `begin_keywords directive. +`begin_keywords "1800-2017" +`begin_keywords "1800-2012" +`begin_keywords "1800-2009" +`begin_keywords "1800-2005" +`begin_keywords "1364-2005" +`begin_keywords "1364-2001" +`begin_keywords "1364-2001-noconfig" +`begin_keywords "1364-1995" +`end_keywords +`end_keywords +`end_keywords +`end_keywords +`end_keywords +`end_keywords +`end_keywords +`end_keywords +// This file should be emitted from the preprocessor unchanged. diff --git a/sv-parser-pp/testcases/line.sv b/sv-parser-pp/testcases/line.sv new file mode 100644 index 0000000..120e380 --- /dev/null +++ b/sv-parser-pp/testcases/line.sv @@ -0,0 +1,34 @@ +// IEEE1800-2017 Clause 22.12 +// The `line compiler directive can be used to specify the original source code +// line number and file name. +// This allows the location in the original file to be maintained if another +// process modifies the source. After the new line number and file name are +// specified, the compiler can correctly refer to the original source location. +// However, a tool is not required to produce `line directives. These +// directives are not intended to be inserted manually into the code, although +// they can be. +// The compiler shall maintain the current line number and file name of the +// file being compiled. The `line directive shall set the line number and file +// name of the following line to those specified in the directive. +// The directive can be specified anywhere within the SystemVerilog source +// description. However, only white space may appear on the same line as the +// `line directive. Comments are not allowed on the same line as a `line +// directive. All parameters in the `line directive are required. The results +// of this directive are not affected by the `resetall directive. +// +// The number parameter shall be a positive integer that specifies the new line +// number of the following text line. The filename parameter shall be a string +// literal that is treated as the new name of the file. The filename can also +// be a full or relative path name. The level parameter shall be 0, 1, or 2. +// The value 1 indicates that the following line is the first line after an +// include file has been entered. The value 2 indicates that the following +// line is the first line after an include file has been exited. The value +// 0 indicates any other line. + +`line 3 "orig.v" 2 +// This line is line 3 of orig.v after exiting include file + +`line 999 "foo.sv" 2 +`line 888 "foo.sv" 1 +`line 777 "foo.sv" 0 +// This file should be emitted from the preprocessor unchanged. diff --git a/sv-parser-pp/testcases/macro_FILE.sv b/sv-parser-pp/testcases/macro_FILE.sv new file mode 100644 index 0000000..2ae8404 --- /dev/null +++ b/sv-parser-pp/testcases/macro_FILE.sv @@ -0,0 +1,24 @@ +// __FILE__ = `__FILE__ + +`ifdef __FILE__ +// This block SHOULD be emitted from the preprocessor. +`elsif UNDEFINED +// NOT emitted. +`endif + +`ifndef __FILE__ +// This block should NOT be emitted from the preprocessor. +// However, following (conditional) definition should make it through the +// preprocessor parsing stage without error. +`define __FILE__ "(null)" +`elsif UNDEFINED +// Emitted instead. +`endif + +// The following define should have no effect. +`define __FILE__ "FOO" + +// The following undef should have no effect. +`undef __FILE__ + +// NOTE: Comparison against expected value are destined to fail in testcase. diff --git a/sv-parser-pp/testcases/macro_LINE.sv b/sv-parser-pp/testcases/macro_LINE.sv new file mode 100644 index 0000000..76de077 --- /dev/null +++ b/sv-parser-pp/testcases/macro_LINE.sv @@ -0,0 +1,30 @@ +// __LINE__ = `__LINE__ + +`ifdef __LINE__ +// This block SHOULD be emitted from the preprocessor. +`elsif UNDEFINED +// NOT emitted. +`endif + +`ifndef __LINE__ +// This block should NOT be emitted from the preprocessor. +// However, following (conditional) definition should make it through the +// preprocessor parsing stage without error. +`define __LINE__ -1 +`elsif UNDEFINED +// Emitted instead. +`endif + +// The following define should have no effect. +`define __LINE__ -2 + +// The following undef should have no effect. +`undef __LINE__ + +module M; + initial + if (`__LINE__ == 28) // Should be "26 == 28". + $display("PASS"); + else if (`__LINE__ == 28) // Should be "28 == 28". + $display("FAIL"); +endmodule diff --git a/sv-parser-pp/testcases/macro_arguments.sv b/sv-parser-pp/testcases/macro_arguments.sv new file mode 100644 index 0000000..fae8300 --- /dev/null +++ b/sv-parser-pp/testcases/macro_arguments.sv @@ -0,0 +1,23 @@ +// Macro with parameters with usage spread over multiple lines. +// Final line of macro is line14. +// Argument value `clk` is equal to its name. +// Argument value of exp contains matching brackets and parentheses. +// Bracketed value of msg is required to avoid being parsed as a parameterized +// macro instead of argumnts to $display. +// NOTE: Trailing whitespace is not exercised here, i.e. continuations +// immediately follow non-whitespace. +`define disp(clk, exp, msg)\ + always @(posedge clk)\ + if (exp) begin\ + $display msg;\ + end\ + +module M (); + +`disp( + clk, + !(a[i].b && c[i]), + ("xxx(()[]]{}}}", a[i].b, c[i]) +); // NOTE: Semi-colon is unnecessary. + +endmodule diff --git a/sv-parser-pp/testcases/macro_basic.sv b/sv-parser-pp/testcases/macro_basic.sv new file mode 100644 index 0000000..17c60ca --- /dev/null +++ b/sv-parser-pp/testcases/macro_basic.sv @@ -0,0 +1,6 @@ +`define A aaa +module M; +`A#() a0 (.*); // No trailing whitespace. +`A #() a1 (.*); // Trailing 1 space. +`A #() a2 (.*); // Trailing 2 spaces. +endmodule diff --git a/sv-parser-pp/testcases/macro_comment.sv b/sv-parser-pp/testcases/macro_comment.sv new file mode 100644 index 0000000..af7dafa --- /dev/null +++ b/sv-parser-pp/testcases/macro_comment.sv @@ -0,0 +1,16 @@ +/* IEEE1800-2017 Clause 22.5.1, page 676 +If a one-line comment (that is, a comment specified with the characters //) is +included in the text, then the comment shall not become part of the substituted +text. +*/ + +// A has no comment +// B has a comment after 1 space +// C has a comment after 3 spaces +`define A 11 +`define B 22 // Comment not included in macro, but whitespace before `//` is. +`define C 33 // Comment not included in macro, but whitespace before `//` is. + +interface A #(p=`A) (); endinterface +interface B #(p=`B) (); endinterface +interface C #(p=`C) (); endinterface diff --git a/sv-parser-pp/testcases/macro_delimiters.sv b/sv-parser-pp/testcases/macro_delimiters.sv new file mode 100644 index 0000000..8779224 --- /dev/null +++ b/sv-parser-pp/testcases/macro_delimiters.sv @@ -0,0 +1,13 @@ +// Multi-line macro defined with 2 trailing spaces before initial continuation. +// First line has no trailing space, second has trailing space, third is end. +// Macro contains 2-space indent, so indented usage gets extra. +// Delimiters (``) used before and after arguments. +`define connect(NAME, INDEX = 0) \ + assign NAME``_``INDEX``__x = NAME[INDEX].x;\ + assign NAME``_``INDEX``__y = NAME[INDEX].y; \ + assign NAME``_``INDEX``__z = NAME[INDEX].z; + +module M (); + `connect(a) + `connect(a, 1) +endmodule diff --git a/sv-parser-pp/testcases/macro_identifier.sv b/sv-parser-pp/testcases/macro_identifier.sv new file mode 100644 index 0000000..321f611 --- /dev/null +++ b/sv-parser-pp/testcases/macro_identifier.sv @@ -0,0 +1,10 @@ +`define A "aaa" +`define \B "bbb" +module M; + initial begin + $display(`A); + $display(`\A ); + $display(`B); + $display(`\B ); + end +endmodule diff --git a/sv-parser-pp/testcases/macro_multiline_comment.sv b/sv-parser-pp/testcases/macro_multiline_comment.sv new file mode 100644 index 0000000..d5488cb --- /dev/null +++ b/sv-parser-pp/testcases/macro_multiline_comment.sv @@ -0,0 +1,16 @@ +// Leading whitespace on 4 lines. +// initial \ Space before line continuation. +// begin\ No space before line continuation. +// $display(); // comment \ Continuation at end of comment. +// end +// NOTE: Trailing whitespace on lines ending `initial ` and `$display(); `. + +`define A \ + initial \ + begin\ + $display(); // comment \ + end + +module M; +`A +endmodule diff --git a/sv-parser-pp/testcases/test7.sv b/sv-parser-pp/testcases/macro_recursion_direct.sv similarity index 100% rename from sv-parser-pp/testcases/test7.sv rename to sv-parser-pp/testcases/macro_recursion_direct.sv diff --git a/sv-parser-pp/testcases/test8.sv b/sv-parser-pp/testcases/macro_recursion_indirect.sv similarity index 100% rename from sv-parser-pp/testcases/test8.sv rename to sv-parser-pp/testcases/macro_recursion_indirect.sv diff --git a/sv-parser-pp/testcases/pragma.sv b/sv-parser-pp/testcases/pragma.sv new file mode 100644 index 0000000..7fc9e9c --- /dev/null +++ b/sv-parser-pp/testcases/pragma.sv @@ -0,0 +1,46 @@ +// IEEE1800-2017 Clause 22.11 +// The `pragma directive is a structured specification that alters +// interpretation of the SystemVerilog source. The specification introduced by +// this directive is referred to as a pragma. The effect of pragmas other than +// those specified in this standard is implementation-specific. +`pragma foo +`pragma foo bar +`pragma foo bar,baz +`pragma foo bar, baz +// The reset and resetall pragmas shall restore the default values and state of +// pragma_keywords associated with the affected pragmas. These default values +// shall be the values that the tool defines before any SystemVerilog text has +// been processed. The reset pragma shall reset the state for all pragma_names +// that appear as pragma_keywords in the directive. The resetall pragma shall +// reset the state of all pragma_names recognized by the implementation. +`pragma reset bar +`pragma reset bar,baz +`pragma reset bar, baz + +// Protected envelopes specify a region of text that shall be transformed prior +// to analysis by the source language processor. These regions of text are +// structured to provide the source language processor with the specification +// of the cryptographic algorithm, key, envelope attributes, and textual design +// data. +// The following example shows the use of the protect pragma to specify +// encryption of design data. The encryption method is a simple substitution +// cipher where each alphabetic character is replaced with the 13th character +// in alphabetic sequence, commonly referred to as "rot13." Nonalphabetic +// characters are not substituted. The following design data contain an +// encryption envelope that specifies the desired protection. +module secret (a, b); + input a; + output b; +`pragma protect encoding=(enctype="raw") +`pragma protect data_method="x-caesar", data_keyname="rot13", begin +`pragma protect runtime_license=(library="lic.so",feature="runSecret",entry="chk", match=42) + logic b; + initial begin + b = 0; + end + always begin + #5 b = a; + end +`pragma protect end +endmodule +// This file should be emitted from the preprocessor unchanged. diff --git a/sv-parser-pp/testcases/resetall.sv b/sv-parser-pp/testcases/resetall.sv new file mode 100644 index 0000000..b81eb90 --- /dev/null +++ b/sv-parser-pp/testcases/resetall.sv @@ -0,0 +1,16 @@ +// IEEE1800-2017 Clause 22.3 +// When `resetall compiler directive is encountered during compilation, all +// compiler directives are set to the default values. This is useful for +// ensuring that only directives that are desired in compiling a particular +// source file are active. +// The recommended usage is to place `resetall at the beginning of each source +// text file, followed immediately by the directives desired in the file. +// It shall be illegal for the `resetall directive to be specified within +// a design element. +// Not all compiler directives have a default value (e.g., `define and +// `include). Directives that do not have a default are not affected by +// `resetall. +`resetall // Comment +`resetall// Comment +`resetall +// This file should be emitted from the preprocessor unchanged. diff --git a/sv-parser-pp/testcases/test10.sv b/sv-parser-pp/testcases/test10.sv deleted file mode 100644 index 8c2a37c..0000000 --- a/sv-parser-pp/testcases/test10.sv +++ /dev/null @@ -1,2 +0,0 @@ -module and_op (a, b, c); -`include "test2.svh" endmodule diff --git a/sv-parser-pp/testcases/test11.sv b/sv-parser-pp/testcases/test11.sv deleted file mode 100644 index 911ec70..0000000 --- a/sv-parser-pp/testcases/test11.sv +++ /dev/null @@ -1,6 +0,0 @@ -module a; -initial begin - if (`__LINE__ == 0) begin - end -end -endmodule diff --git a/sv-parser-pp/testcases/test13.sv b/sv-parser-pp/testcases/test13.sv deleted file mode 100644 index cda8549..0000000 --- a/sv-parser-pp/testcases/test13.sv +++ /dev/null @@ -1,3 +0,0 @@ -`define NAME 42 // Comment -interface foo #(WIDTH = `NAME) (); -endinterface diff --git a/sv-parser-pp/testcases/test15.sv b/sv-parser-pp/testcases/test15.sv deleted file mode 100644 index 083382a..0000000 --- a/sv-parser-pp/testcases/test15.sv +++ /dev/null @@ -1,4 +0,0 @@ -`define MOD_INST u_mysubmod -module mymod; -mysubmod `MOD_INST (); -endmodule diff --git a/sv-parser-pp/testcases/test16.sv b/sv-parser-pp/testcases/test16.sv deleted file mode 100644 index 5c1230e..0000000 --- a/sv-parser-pp/testcases/test16.sv +++ /dev/null @@ -1,10 +0,0 @@ -module a; -`define HELLO0 "HELLO" -`define \HELLO1 "HELLO" -initial begin -$display(`HELLO0); -$display(`\HELLO0 ); -$display(`HELLO1); -$display(`\HELLO1 ); -end -endmodule diff --git a/sv-parser-pp/testcases/test17.sv b/sv-parser-pp/testcases/test17.sv deleted file mode 100644 index 258b0b4..0000000 --- a/sv-parser-pp/testcases/test17.sv +++ /dev/null @@ -1,9 +0,0 @@ -`define A \ - initial begin // comment \ - end - -module test(); - -`A - -endmodule diff --git a/sv-parser-pp/testcases/test21.sv b/sv-parser-pp/testcases/test21.sv deleted file mode 100644 index 390e540..0000000 --- a/sv-parser-pp/testcases/test21.sv +++ /dev/null @@ -1,17 +0,0 @@ -//top -`resetall -`timescale 10 us / 100 ns -`default_nettype wire -//first -`default_nettype none//middle -//last -`unconnected_drive pull0 -`unconnected_drive pull1 -`nounconnected_drive -`celldefine -`endcelldefine -`pragma foo -`pragma foo bar -`line 5 "foo" 0 -`begin_keywords "1800-2017" -`end_keywords diff --git a/sv-parser-pp/testcases/test3.sv b/sv-parser-pp/testcases/test3.sv deleted file mode 100644 index f28b184..0000000 --- a/sv-parser-pp/testcases/test3.sv +++ /dev/null @@ -1,10 +0,0 @@ -`define connect(NAME, INDEX = 0) \ - assign NAME``_``INDEX``__x = NAME[INDEX].x; \ - assign NAME``_``INDEX``__y = NAME[INDEX].y; - -module a (); - - `connect(a) - `connect(a, 1) - -endmodule diff --git a/sv-parser-pp/testcases/test4.sv b/sv-parser-pp/testcases/test4.sv deleted file mode 100644 index 7466db6..0000000 --- a/sv-parser-pp/testcases/test4.sv +++ /dev/null @@ -1,16 +0,0 @@ -`define disp(clk, exp, msg) \ - always @(posedge clk) begin \ - if (!(exp)) begin \ - $display msg; \ - end \ - end \ - -module a (); - -`disp( - clk, - !(a[i].b && c[i]), - ("xxx(()[]]{}}}", a[i].b, c[i]) -); - -endmodule diff --git a/sv-parser-pp/testcases/test5.sv b/sv-parser-pp/testcases/test5.sv deleted file mode 100644 index 662575d..0000000 --- a/sv-parser-pp/testcases/test5.sv +++ /dev/null @@ -1,10 +0,0 @@ -module a; -`define HI Hello -`define LO "`HI, world" -`define H(x) "Hello, x" -initial begin -$display("`HI, world"); -$display(`LO); -$display(`H(world)); -end -endmodule diff --git a/sv-parser-pp/testcases/test9.sv b/sv-parser-pp/testcases/test9.sv deleted file mode 100644 index ad10dda..0000000 --- a/sv-parser-pp/testcases/test9.sv +++ /dev/null @@ -1,3 +0,0 @@ -module and_op (a, b, c); -`include "test2.svh" `include "test2.svh" -endmodule diff --git a/sv-parser-pp/testcases/timescale.sv b/sv-parser-pp/testcases/timescale.sv new file mode 100644 index 0000000..89822f5 --- /dev/null +++ b/sv-parser-pp/testcases/timescale.sv @@ -0,0 +1,17 @@ +// IEEE1800-2017 Clause 22.7 +// This directive specifies the time unit and time precision of the design +// elements that follow it. The time unit is the unit of measurement for time +// values such as the simulation time and delay values. +// The `timescale compiler directive specifies the default unit of measurement +// for time and delay values and the degree of accuracy for delays in all +// design elements that follow this directive, and that do not have timeunit +// and timeprecision constructs specified within the design element, until +// another `timescale compiler directive is read. +// The integers in these arguments specify an order of magnitude for the size +// of the value; the valid integers are 1, 10, and 100. +// The character strings represent units of measurement; the valid character +// strings are s, ms, us, ns, ps, and fs. +`timescale 1 s / 10 ms +`timescale 10 us / 100 ns +`timescale 100 ps / 100 fs +// This file should be emitted from the preprocessor unchanged. diff --git a/sv-parser-pp/testcases/unconnected_drive.sv b/sv-parser-pp/testcases/unconnected_drive.sv new file mode 100644 index 0000000..e48f549 --- /dev/null +++ b/sv-parser-pp/testcases/unconnected_drive.sv @@ -0,0 +1,13 @@ +// IEEE1800-2017 Clause 22.9 +// The directive `unconnected_drive takes one of two arguments - pull1 or +// pull 0. When pull1 is specified, unconnected ports are pulled down. It is +// advisable to pair each `unconnected_drive with a +// `nounconnected_drive, but it is not required. The latest occurrence of +// either directive in the source controls what happens to unconnected ports. +// These directives shall be specified outside the design element declarations. +// The `resetall directive includes the effects of a `nounconnected +// directive. +`unconnected_drive pull0 +`unconnected_drive pull1 +`nounconnected_drive +// This file should be emitted from the preprocessor unchanged. diff --git a/sv-parser-pp/testcases/undef.sv b/sv-parser-pp/testcases/undef.sv new file mode 100644 index 0000000..76d3731 --- /dev/null +++ b/sv-parser-pp/testcases/undef.sv @@ -0,0 +1,28 @@ +// IEEE1800-2017 Clause 22.5.2 +// The directive `undef shall undefine the specified text macro if previously +// defined by a `define compiler directive within the compilation unit. An +// attempt to undefine a text macro that was not previously defined using a +// `define compiler directive can issue a warning. +`undef FOO +`undef FOO// Comment +`undef FOO // Comment + +`define FOO foo +`ifdef FOO +// AAA +// This block SHOULD be emitted from the preprocessor. +`endif +`ifndef FOO +// AAA +// This block should NOT be emitted from the preprocessor. +`endif + +`undef FOO +`ifdef FOO +// BBB +// This block should NOT be emitted from the preprocessor. +`endif +`ifndef FOO +// BBB +// This block SHOULD be emitted from the preprocessor. +`endif diff --git a/sv-parser-pp/testcases/undefineall.sv b/sv-parser-pp/testcases/undefineall.sv new file mode 100644 index 0000000..734fb4a --- /dev/null +++ b/sv-parser-pp/testcases/undefineall.sv @@ -0,0 +1,45 @@ +// IEEE1800-2017 Clause 22.5.3 +// The directive `undefineall directive shall undefine all text macros +// previously defined by `define compiler directives within the compilation +// unit. This directive takes no argument and may appear anywhere in the source +// description. +`undefineall +`undefineall// Comment +`undefineall // Comment + +`define FOO foo +`define BAR bar +`ifdef FOO +// AAA +// This block SHOULD be emitted from the preprocessor. +`endif +`ifndef FOO +// AAA +// This block should NOT be emitted from the preprocessor. +`endif +`ifdef BAR +// BBB +// This block SHOULD be emitted from the preprocessor. +`endif +`ifndef BAR +// BBB +// This block should NOT be emitted from the preprocessor. +`endif + +`undefineall +`ifdef FOO +// CCC +// This block should NOT be emitted from the preprocessor. +`endif +`ifndef FOO +// CCC +// This block SHOULD be emitted from the preprocessor. +`endif +`ifdef BAR +// DDD +// This block should NOT be emitted from the preprocessor. +`endif +`ifndef BAR +// DDD +// This block SHOULD be emitted from the preprocessor. +`endif diff --git a/sv-parser/examples/parse_sv.rs b/sv-parser/examples/parse_sv.rs index c37d866..c140ace 100644 --- a/sv-parser/examples/parse_sv.rs +++ b/sv-parser/examples/parse_sv.rs @@ -62,7 +62,13 @@ fn main() { let mut exit = 0; for path in &opt.files { if opt.pp { - match preprocess(&path, &defines, &opt.includes, false, false) { + match preprocess( + &path, + &defines, + &opt.includes, + false, // strip_comments + false, // ignore_include + ) { Ok((preprocessed_text, new_defines)) => { println!("{}", preprocessed_text.text()); defines = new_defines; diff --git a/sv-parser/src/lib.rs b/sv-parser/src/lib.rs index 40b1c47..e8cf2ba 100644 --- a/sv-parser/src/lib.rs +++ b/sv-parser/src/lib.rs @@ -131,7 +131,13 @@ pub fn parse_sv, U: AsRef, V: BuildHasher>( ignore_include: bool, allow_incomplete: bool, ) -> Result<(SyntaxTree, Defines), Error> { - let (text, defines) = preprocess(path, pre_defines, include_paths, false, ignore_include)?; + let (text, defines) = preprocess( + path, + pre_defines, + include_paths, + false, // strip_comments + ignore_include, + )?; parse_sv_pp(text, defines, allow_incomplete) } @@ -188,8 +194,9 @@ pub fn parse_sv_str, U: AsRef, V: BuildHasher>( pre_defines, include_paths, ignore_include, - false, - 0, + false, // strip_comments + 0, // resolve_depth + 0, // include_depth )?; parse_sv_pp(text, defines, allow_incomplete) } @@ -201,7 +208,13 @@ pub fn parse_lib, U: AsRef, V: BuildHasher>( ignore_include: bool, allow_incomplete: bool, ) -> Result<(SyntaxTree, Defines), Error> { - let (text, defines) = preprocess(path, pre_defines, include_paths, false, ignore_include)?; + let (text, defines) = preprocess( + path, + pre_defines, + include_paths, + false, // strip_comments + ignore_include, + )?; parse_lib_pp(text, defines, allow_incomplete) } @@ -219,8 +232,9 @@ pub fn parse_lib_str, U: AsRef, V: BuildHasher>( pre_defines, include_paths, ignore_include, - false, - 0, + false, // strip_comments + 0, // resolve_depth + 0, // include_depth )?; parse_lib_pp(text, defines, allow_incomplete) }