Wrap FunctionName in a newtype struct.

Function names display differently than normal strings since they need
quotes and escaping.

Move the FunctionName type into its own module.
This commit is contained in:
Jakob Stoklund Olesen
2016-09-19 14:52:48 -07:00
parent 6fb566fa85
commit 388154e06b
7 changed files with 91 additions and 59 deletions

View File

@@ -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

View File

@@ -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: Into<String>>(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\"");
}
}

View File

@@ -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.

View File

@@ -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;

View File

@@ -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

View File

@@ -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<bool, Error> {
@@ -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));

View File

@@ -470,11 +470,11 @@ impl<'a> Parser<'a> {
//
// function ::= "function" * name signature { ... }
//
fn parse_function_name(&mut self) -> Result<String> {
fn parse_function_name(&mut self) -> Result<FunctionName> {
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");
}
}