1 //! Parsed representation of `set` and `isa` commands.
2 //!
3 //! A test case file can contain `set` commands that set ISA-independent settings, and it can
4 //! contain `isa` commands that select an ISA and applies ISA-specific settings.
5 //!
6 //! If a test case file contains `isa` commands, the tests will only be run against the specified
7 //! ISAs. If the file contains no `isa` commands, the tests will be run against all supported ISAs.
8
9 use crate::error::{Location, ParseError};
10 use crate::testcommand::TestOption;
11 use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};
12 use cranelift_codegen::settings::{Configurable, Flags, SetError};
13
14 /// The ISA specifications in a `.clif` file.
15 pub enum IsaSpec {
16 /// The parsed file does not contain any `isa` commands, but it may contain `set` commands
17 /// which are reflected in the finished `Flags` object.
18 None(Flags),
19
20 /// The parsed file does contain `isa` commands.
21 /// Each `isa` command is used to configure a `TargetIsa` trait object.
22 Some(Vec<OwnedTargetIsa>),
23 }
24
25 impl IsaSpec {
26 /// If the `IsaSpec` contains exactly 1 `TargetIsa` we return a reference to it
unique_isa(&self) -> Option<&dyn TargetIsa>27 pub fn unique_isa(&self) -> Option<&dyn TargetIsa> {
28 if let Self::Some(ref isa_vec) = *self {
29 if isa_vec.len() == 1 {
30 return Some(&*isa_vec[0]);
31 }
32 }
33 None
34 }
35 }
36
37 /// An error type returned by `parse_options`.
38 pub enum ParseOptionError {
39 /// A generic ParseError.
40 Generic(ParseError),
41
42 /// An unknown flag was used, with the given name at the given location.
43 UnknownFlag {
44 /// Location where the flag was given.
45 loc: Location,
46 /// Name of the unknown flag.
47 name: String,
48 },
49
50 /// An unknown value was used, with the given name at the given location.
51 UnknownValue {
52 /// Location where the flag was given.
53 loc: Location,
54 /// Name of the unknown value.
55 name: String,
56 /// Value of the unknown value.
57 value: String,
58 },
59 }
60
61 impl From<ParseOptionError> for ParseError {
from(err: ParseOptionError) -> Self62 fn from(err: ParseOptionError) -> Self {
63 match err {
64 ParseOptionError::Generic(err) => err,
65 ParseOptionError::UnknownFlag { loc, name } => Self {
66 location: loc,
67 message: format!("unknown setting '{name}'"),
68 is_warning: false,
69 },
70 ParseOptionError::UnknownValue { loc, name, value } => Self {
71 location: loc,
72 message: format!("unknown setting '{name}={value}'"),
73 is_warning: false,
74 },
75 }
76 }
77 }
78
79 macro_rules! option_err {
80 ( $loc:expr, $fmt:expr, $( $arg:expr ),+ ) => {
81 Err($crate::ParseOptionError::Generic($crate::ParseError {
82 location: $loc.clone(),
83 message: format!( $fmt, $( $arg ),+ ),
84 is_warning: false,
85 }))
86 };
87 }
88
89 /// Parse an iterator of command line options and apply them to `config`.
90 ///
91 /// Note that parsing terminates after the first error is encountered.
parse_options<'a, I>( iter: I, config: &mut dyn Configurable, loc: Location, ) -> Result<(), ParseOptionError> where I: Iterator<Item = &'a str>,92 pub fn parse_options<'a, I>(
93 iter: I,
94 config: &mut dyn Configurable,
95 loc: Location,
96 ) -> Result<(), ParseOptionError>
97 where
98 I: Iterator<Item = &'a str>,
99 {
100 for opt in iter {
101 parse_option(opt, config, loc)?;
102 }
103 Ok(())
104 }
105
106 /// Parse an single command line options and apply it to `config`.
parse_option( opt: &str, config: &mut dyn Configurable, loc: Location, ) -> Result<(), ParseOptionError>107 pub fn parse_option(
108 opt: &str,
109 config: &mut dyn Configurable,
110 loc: Location,
111 ) -> Result<(), ParseOptionError> {
112 match TestOption::new(opt) {
113 TestOption::Flag(name) => match config.enable(name) {
114 Ok(_) => Ok(()),
115 Err(SetError::BadName(name)) => Err(ParseOptionError::UnknownFlag { loc, name }),
116 Err(_) => option_err!(loc, "not a boolean flag: '{}'", opt),
117 },
118 TestOption::Value(name, value) => match config.set(name, value) {
119 Ok(_) => Ok(()),
120 Err(SetError::BadName(name)) => Err(ParseOptionError::UnknownValue {
121 loc,
122 name,
123 value: value.to_string(),
124 }),
125 Err(SetError::BadType) => option_err!(loc, "invalid setting type: '{}'", opt),
126 Err(SetError::BadValue(expected)) => {
127 option_err!(
128 loc,
129 "invalid setting value for '{}', expected {}",
130 opt,
131 expected
132 )
133 }
134 },
135 }
136 }
137