diff --git a/cranelift/filetests/parser/rewrite.cton b/cranelift/filetests/parser/rewrite.cton index 48ad84869c..9f95cb93fa 100644 --- a/cranelift/filetests/parser/rewrite.cton +++ b/cranelift/filetests/parser/rewrite.cton @@ -29,7 +29,7 @@ ebb100(v20: i32): vx200 = iadd v20, v1000 jump ebb100(v1000) } -; sameln: function "use_value"() { +; sameln: function use_value() { ; nextln: ebb0(vx0: i32): ; nextln: v0 = iadd_imm vx0, 5 ; nextln: v1 = iadd vx0, v0 diff --git a/cranelift/src/libcretonne/ir/funcname.rs b/cranelift/src/libcretonne/ir/funcname.rs new file mode 100644 index 0000000000..0f6f106365 --- /dev/null +++ b/cranelift/src/libcretonne/ir/funcname.rs @@ -0,0 +1,77 @@ +//! Function names. +//! +//! The name of a function doesn't have any meaning to Cretonne which compiles functions +//! independently. + +use std::fmt::{self, Write}; +use std::ascii::AsciiExt; + +/// The name of a function can be any UTF-8 string. +/// +/// Function names are mostly a testing and debugging tool. +/// In particular, `.cton` files use function names to identify functions. +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct FunctionName(String); + +impl FunctionName { + pub fn new>(s: S) -> FunctionName { + FunctionName(s.into()) + } +} + +fn is_id_start(c: char) -> bool { + c.is_ascii() && (c == '_' || c.is_alphabetic()) +} + +fn is_id_continue(c: char) -> bool { + c.is_ascii() && (c == '_' || c.is_alphanumeric()) +} + +// The function name may need quotes if it doesn't parse as an identifier. +fn needs_quotes(name: &str) -> bool { + let mut iter = name.chars(); + if let Some(ch) = iter.next() { + !is_id_start(ch) || !iter.all(is_id_continue) + } else { + // A blank function name needs quotes. + true + } +} + +impl fmt::Display for FunctionName { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if needs_quotes(&self.0) { + try!(f.write_char('"')); + for c in self.0.chars().flat_map(char::escape_default) { + try!(f.write_char(c)); + } + f.write_char('"') + } else { + f.write_str(&self.0) + } + } +} + +#[cfg(test)] +mod tests { + use super::{needs_quotes, FunctionName}; + + #[test] + fn quoting() { + assert_eq!(needs_quotes(""), true); + assert_eq!(needs_quotes("x"), false); + assert_eq!(needs_quotes(" "), true); + assert_eq!(needs_quotes("0"), true); + assert_eq!(needs_quotes("x0"), false); + } + + #[test] + fn escaping() { + assert_eq!(FunctionName::new("").to_string(), "\"\""); + assert_eq!(FunctionName::new("x").to_string(), "x"); + assert_eq!(FunctionName::new(" ").to_string(), "\" \""); + assert_eq!(FunctionName::new(" \n").to_string(), "\" \\n\""); + assert_eq!(FunctionName::new("a\u{1000}v").to_string(), + "\"a\\u{1000}v\""); + } +} diff --git a/cranelift/src/libcretonne/ir/function.rs b/cranelift/src/libcretonne/ir/function.rs index 415e8de9cc..ac6c962861 100644 --- a/cranelift/src/libcretonne/ir/function.rs +++ b/cranelift/src/libcretonne/ir/function.rs @@ -52,7 +52,7 @@ impl Function { /// Create a new empty, anomymous function. pub fn new() -> Function { - Self::with_name_signature(FunctionName::new(), Signature::new()) + Self::with_name_signature(FunctionName::default(), Signature::new()) } /// Get the signature of this function. diff --git a/cranelift/src/libcretonne/ir/mod.rs b/cranelift/src/libcretonne/ir/mod.rs index a26e48f15e..ae16eae735 100644 --- a/cranelift/src/libcretonne/ir/mod.rs +++ b/cranelift/src/libcretonne/ir/mod.rs @@ -10,8 +10,10 @@ pub mod jumptable; pub mod dfg; pub mod layout; pub mod function; +mod funcname; -pub use ir::types::{Type, FunctionName, Signature}; +pub use ir::funcname::FunctionName; +pub use ir::types::{Type, Signature}; pub use ir::entities::{Ebb, Inst, Value, StackSlot, JumpTable}; pub use ir::instructions::{Opcode, InstructionData}; pub use ir::stackslot::StackSlotData; diff --git a/cranelift/src/libcretonne/ir/types.rs b/cranelift/src/libcretonne/ir/types.rs index 12dfc8c6ec..f4e0a6b75f 100644 --- a/cranelift/src/libcretonne/ir/types.rs +++ b/cranelift/src/libcretonne/ir/types.rs @@ -191,12 +191,6 @@ impl Default for Type { // // ====--------------------------------------------------------------------------------------====// -/// The name of a function can be any UTF-8 string. -/// -/// Function names are mostly a testing and debugging tool. In partucular, `.cton` files use -/// function names to identify functions. -pub type FunctionName = String; - /// Function argument extension options. /// /// On some architectures, small integer function arguments are extended to the width of a diff --git a/cranelift/src/libcretonne/write.rs b/cranelift/src/libcretonne/write.rs index aa5076f5d5..c17025d1a8 100644 --- a/cranelift/src/libcretonne/write.rs +++ b/cranelift/src/libcretonne/write.rs @@ -29,30 +29,8 @@ pub fn write_function(w: &mut Write, func: &Function) -> Result { // // ====--------------------------------------------------------------------------------------====// -// The function name may need quotes if it doesn't parse as an identifier. -fn needs_quotes(name: &str) -> bool { - let mut iter = name.chars(); - if let Some(ch) = iter.next() { - !ch.is_alphabetic() || !iter.all(char::is_alphanumeric) - } else { - // A blank function name needs quotes. - true - } -} - -// Use Rust's escape_default which provides a few simple \t \r \n \' \" \\ escapes and uses -// \u{xxxx} for anything else outside the ASCII printable range. -fn escaped(name: &str) -> String { - name.chars().flat_map(char::escape_default).collect() -} - fn write_spec(w: &mut Write, func: &Function) -> Result { - let sig = func.own_signature(); - if !needs_quotes(&func.name) { - write!(w, "function {}{}", func.name, sig) - } else { - write!(w, "function \"{}\"{}", escaped(&func.name), sig) - } + write!(w, "function {}{}", func.name, func.own_signature()) } fn write_preamble(w: &mut Write, func: &Function) -> result::Result { @@ -208,34 +186,15 @@ pub fn write_instruction(w: &mut Write, func: &Function, inst: Inst) -> Result { #[cfg(test)] mod tests { - use super::{needs_quotes, escaped}; - use ir::{Function, StackSlotData}; + use ir::{Function, FunctionName, StackSlotData}; use ir::types; - #[test] - fn quoting() { - assert_eq!(needs_quotes(""), true); - assert_eq!(needs_quotes("x"), false); - assert_eq!(needs_quotes(" "), true); - assert_eq!(needs_quotes("0"), true); - assert_eq!(needs_quotes("x0"), false); - } - - #[test] - fn escaping() { - assert_eq!(escaped(""), ""); - assert_eq!(escaped("x"), "x"); - assert_eq!(escaped(" "), " "); - assert_eq!(escaped(" \n"), " \\n"); - assert_eq!(escaped("a\u{1000}v"), "a\\u{1000}v"); - } - #[test] fn basic() { let mut f = Function::new(); assert_eq!(f.to_string(), "function \"\"() {\n}\n"); - f.name.push_str("foo"); + f.name = FunctionName::new("foo".to_string()); assert_eq!(f.to_string(), "function foo() {\n}\n"); f.stack_slots.push(StackSlotData::new(4)); diff --git a/cranelift/src/libreader/parser.rs b/cranelift/src/libreader/parser.rs index d76392d0f2..45177aace7 100644 --- a/cranelift/src/libreader/parser.rs +++ b/cranelift/src/libreader/parser.rs @@ -470,11 +470,11 @@ impl<'a> Parser<'a> { // // function ::= "function" * name signature { ... } // - fn parse_function_name(&mut self) -> Result { + fn parse_function_name(&mut self) -> Result { match self.token() { Some(Token::Identifier(s)) => { self.consume(); - Ok(s.to_string()) + Ok(FunctionName::new(s)) } _ => err!(self.loc, "expected function name"), } @@ -1161,7 +1161,7 @@ mod tests { }") .parse_function() .unwrap(); - assert_eq!(func.name, "foo"); + assert_eq!(func.name.to_string(), "foo"); let mut iter = func.stack_slots.keys(); let ss0 = iter.next().unwrap(); assert_eq!(ss0.to_string(), "ss0"); @@ -1190,7 +1190,7 @@ mod tests { }") .parse_function() .unwrap(); - assert_eq!(func.name, "ebbs"); + assert_eq!(func.name.to_string(), "ebbs"); let mut ebbs = func.layout.ebbs(); @@ -1219,7 +1219,7 @@ mod tests { ; More trailing.") .parse_function() .unwrap(); - assert_eq!(&func.name, "comment"); + assert_eq!(func.name.to_string(), "comment"); assert_eq!(comments.len(), 8); // no 'before' comment. assert_eq!(comments[0], Comment { @@ -1252,6 +1252,6 @@ mod tests { assert_eq!(tf.commands[0].command, "cfg"); assert_eq!(tf.commands[1].command, "verify"); assert_eq!(tf.functions.len(), 1); - assert_eq!(tf.functions[0].0.name, "comment"); + assert_eq!(tf.functions[0].0.name.to_string(), "comment"); } }