Browse Source

Add parsing functionality for run commands

pull/1472/head
Andrew Brown 5 years ago
parent
commit
3d5ff8dc3d
  1. 227
      cranelift/reader/src/parser.rs

227
cranelift/reader/src/parser.rs

@ -3,6 +3,7 @@
use crate::error::{Location, ParseError, ParseResult};
use crate::isaspec;
use crate::lexer::{LexError, Lexer, LocatedError, LocatedToken, Token};
use crate::run_command::{Comparison, DataValue, Invocation, RunCommand};
use crate::sourcemap::SourceMap;
use crate::testcommand::TestCommand;
use crate::testfile::{Comment, Details, Feature, TestFile};
@ -2392,6 +2393,160 @@ impl<'a> Parser<'a> {
Ok(args)
}
/// Parse a CLIF run command.
///
/// run-command ::= "run" [":" invocation comparison expected]
/// \ "print" [":" invocation]
fn parse_run_command(&mut self, sig: &Signature) -> ParseResult<RunCommand> {
// skip semicolon
match self.token() {
Some(Token::Identifier("run")) => {
self.consume();
if self.optional(Token::Colon) {
let invocation = self.parse_run_invocation(sig)?;
let comparison = self.parse_run_comparison()?;
let expected = self.parse_run_returns(sig)?;
Ok(RunCommand::Run(invocation, comparison, expected))
} else if sig.params.is_empty()
&& sig.returns.len() == 1
&& sig.returns[0].value_type.is_bool()
{
// To match the existing run behavior that does not require an explicit
// invocation, we create an invocation from a function like `() -> b*` and
// compare it to `true`.
let invocation = Invocation::new("default", vec![]);
let expected = vec![DataValue::B(true)];
let comparison = Comparison::Equals;
Ok(RunCommand::Run(invocation, comparison, expected))
} else {
Err(self.error("unable to parse the run command"))
}
}
Some(Token::Identifier("print")) => {
self.consume();
if self.optional(Token::Colon) {
Ok(RunCommand::Print(self.parse_run_invocation(sig)?))
} else if sig.params.is_empty() {
// To allow printing of functions like `() -> *`, we create a no-arg invocation.
let invocation = Invocation::new("default", vec![]);
Ok(RunCommand::Print(invocation))
} else {
Err(self.error("unable to parse the print command"))
}
}
_ => Err(self.error("expected a 'run:' or 'print:' command")),
}
}
/// Parse the invocation of a CLIF function.
///
/// This is different from parsing a CLIF `call`; it is used in parsing run commands like
/// `run: %fn(42, 4.2) == false`.
///
/// invocation ::= name "(" [data-value-list] ")"
fn parse_run_invocation(&mut self, sig: &Signature) -> ParseResult<Invocation> {
if let Some(Token::Name(name)) = self.token() {
self.consume();
self.match_token(
Token::LPar,
"expected invocation parentheses, e.g. %fn(...)",
)?;
let args = self.parse_data_value_list(
&sig.params.iter().map(|a| a.value_type).collect::<Vec<_>>(),
)?;
self.match_token(
Token::RPar,
"expected invocation parentheses, e.g. %fn(...)",
)?;
Ok(Invocation::new(name, args))
} else {
Err(self.error("expected a function name, e.g. %my_fn"))
}
}
/// Parse a comparison operator for run commands.
///
/// comparison ::= "==" | "!="
fn parse_run_comparison(&mut self) -> ParseResult<Comparison> {
if self.optional(Token::Equal) {
self.match_token(Token::Equal, "expected another =")?;
Ok(Comparison::Equals)
} else if self.optional(Token::Not) {
self.match_token(Token::Equal, "expected a =")?;
Ok(Comparison::NotEquals)
} else {
Err(self.error("unable to parse a valid comparison operator"))
}
}
/// Parse the expected return values of a run invocation.
///
/// expected ::= "[" "]"
/// | data-value
/// | "[" data-value-list "]"
fn parse_run_returns(&mut self, sig: &Signature) -> ParseResult<Vec<DataValue>> {
if sig.returns.len() != 1 {
self.match_token(Token::LBracket, "expected a left bracket [")?;
}
let returns = self
.parse_data_value_list(&sig.returns.iter().map(|a| a.value_type).collect::<Vec<_>>())?;
if sig.returns.len() != 1 {
self.match_token(Token::RBracket, "expected a right bracket ]")?;
}
Ok(returns)
}
/// Parse a comma-separated list of data values.
///
/// data-value-list ::= [data-value {"," data-value-list}]
fn parse_data_value_list(&mut self, types: &[Type]) -> ParseResult<Vec<DataValue>> {
let mut values = vec![];
for ty in types.iter().take(1) {
values.push(self.parse_data_value(*ty)?);
}
for ty in types.iter().skip(1) {
self.match_token(
Token::Comma,
"expected a comma between invocation arguments",
)?;
values.push(self.parse_data_value(*ty)?);
}
Ok(values)
}
/// Parse a data value; e.g. `42`, `4.2`, `true`.
///
/// data-value-list ::= [data-value {"," data-value-list}]
fn parse_data_value(&mut self, ty: Type) -> ParseResult<DataValue> {
let dv = match ty {
I8 => DataValue::from(self.match_imm8("expected a i8")?),
I16 => DataValue::from(self.match_imm16("expected an i16")?),
I32 => DataValue::from(self.match_imm32("expected an i32")?),
I64 => DataValue::from(Into::<i64>::into(self.match_imm64("expected an i64")?)),
F32 => DataValue::from(f32::from_bits(self.match_ieee32("expected an f32")?.bits())),
F64 => DataValue::from(f64::from_bits(self.match_ieee64("expected an f64")?.bits())),
_ if ty.is_vector() => {
let as_vec = self.match_constant_data(ty)?.into_vec();
if as_vec.len() == 16 {
let mut as_array = [0; 16];
as_array.copy_from_slice(&as_vec[..16]);
DataValue::from(as_array)
} else {
return Err(self.error("only 128-bit vectors are currently supported"));
}
}
_ if ty.is_bool() && !ty.is_vector() => {
DataValue::from(self.match_bool("expected a boolean")?)
}
_ => return Err(self.error(&format!("don't know how to parse data values of: {}", ty))),
};
Ok(dv)
}
// Parse the operands following the instruction opcode.
// This depends on the format of the opcode.
fn parse_inst_operands(
@ -3529,4 +3684,76 @@ mod tests {
[1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
)
}
#[test]
fn parse_run_commands() {
// Helper for creating signatures.
fn sig(ins: &[Type], outs: &[Type]) -> Signature {
let mut sig = Signature::new(CallConv::Fast);
for i in ins {
sig.params.push(AbiParam::new(*i));
}
for o in outs {
sig.returns.push(AbiParam::new(*o));
}
sig
}
// Helper for parsing run commands.
fn parse(text: &str, sig: &Signature) -> ParseResult<RunCommand> {
Parser::new(text).parse_run_command(sig)
}
// Check that we can parse and display the same set of run commands.
fn assert_roundtrip(text: &str, sig: &Signature) {
assert_eq!(parse(text, sig).unwrap().to_string(), text);
}
assert_roundtrip("run: %fn0() == 42", &sig(&[], &[I32]));
assert_roundtrip(
"run: %fn0(8, 16, 32, 64) == true",
&sig(&[I8, I16, I32, I64], &[B8]),
);
assert_roundtrip(
"run: %my_func(true) == 0x0f0e0d0c0b0a09080706050403020100",
&sig(&[B32], &[I8X16]),
);
// Verify that default invocations are created when not specified.
assert_eq!(
parse("run", &sig(&[], &[B32])).unwrap().to_string(),
"run: %default() == true"
);
assert_eq!(
parse("print", &sig(&[], &[F32X4, I16X8]))
.unwrap()
.to_string(),
"print: %default()"
);
// Demonstrate some unparseable cases.
assert!(parse("print", &sig(&[I32], &[B32])).is_err());
assert!(parse("run", &sig(&[], &[I32])).is_err());
assert!(parse("print:", &sig(&[], &[])).is_err());
assert!(parse("run: ", &sig(&[], &[])).is_err());
}
#[test]
fn parse_data_values() {
fn parse(text: &str, ty: Type) -> DataValue {
Parser::new(text).parse_data_value(ty).unwrap()
}
assert_eq!(parse("8", I8).to_string(), "8");
assert_eq!(parse("16", I16).to_string(), "16");
assert_eq!(parse("32", I32).to_string(), "32");
assert_eq!(parse("64", I64).to_string(), "64");
assert_eq!(parse("0x32.32", F32).to_string(), "0x1.919000p5");
assert_eq!(parse("0x64.64", F64).to_string(), "0x1.9190000000000p6");
assert_eq!(parse("true", B1).to_string(), "true");
assert_eq!(parse("false", B64).to_string(), "false");
assert_eq!(
parse("[0 1 2 3]", I32X4).to_string(),
"0x03000000020000000100000000"
);
}
}

Loading…
Cancel
Save