Last active
August 29, 2015 14:07
-
-
Save pythonesque/e0bd89f00f64c66de081 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(overloaded_calls)] | |
pub struct G<T, U> { | |
n: T, | |
} | |
impl<T: Add<U, T> + Clone, U> FnMut<(U,), T> for G<T, U> { | |
extern "rust-call" fn call_mut(&mut self, (i,):(U,)) -> T { | |
self.n = self.n + i; | |
self.n.clone() | |
} | |
} | |
pub fn accum<T: Add<T, U> + Clone, U>(n: T) -> G<T, U> { | |
G { n: n } | |
} | |
fn main() { | |
// Deviation: works with all types implementing addition, but not a mixture | |
// of types (it is possible to handle mixed types, but would require type | |
// switching at the moment). | |
let mut g = accum(1f32); | |
g(5.); | |
accum(3i32); | |
println!("{}", g(2.3)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Implements http://rosettacode.org/wiki/S-Expressions | |
extern crate arena; | |
use arena::TypedArena; | |
use std::io; | |
use std::num; | |
use std::char; | |
use std::string; | |
#[deriving(Show)] | |
enum SExp<'a> { | |
F64(f64), | |
List(&'a [SExp<'a>]), | |
Str(&'a str), | |
} | |
#[deriving(Eq,PartialEq,Show)] | |
pub enum Error { | |
NoReprForFloat, | |
StackUnderflow, | |
UnterminatedStringLiteral, | |
IoError(io::IoError), | |
InternalError, | |
IncorrectCloseDelimiter, | |
UnexpectedEOF, | |
ExpectedEOF, | |
} | |
#[deriving(Eq,PartialEq,Show)] | |
enum Token<'a> { | |
ListStart, | |
ListEnd, | |
Literal(&'a str), | |
EOF, | |
} | |
#[deriving(Show)] | |
struct Tokens<'a> { | |
first: Option<char>, | |
rest: &'a str, | |
} | |
impl<'a> Tokens<'a> { | |
fn new(string: &str) -> Tokens { | |
let (ch, s) = string.slice_shift_char(); | |
Tokens { first: ch, rest: string } | |
} | |
fn next(&mut self) -> Result<Token<'a>, Error> { | |
loop { | |
let (ch, s) = self.rest.slice_shift_char(); | |
match ch { | |
// List start | |
Some('(') => { | |
self.rest = s; | |
return Ok(ListStart) | |
}, | |
// List end | |
Some(')') => { | |
self.rest = s; | |
return Ok(ListEnd) | |
}, | |
// Quoted literal start | |
Some('"') => { | |
// Split the string at most once. This lets us get a | |
// reference to the next piece of the string without having | |
// to loop through the string again. | |
let mut iter = s.splitn(1, '"'); | |
let str = match iter.next() { | |
Some(s) => s, | |
None => return Err(InternalError) | |
}; | |
match iter.next() { | |
Some(s) => { | |
self.rest = s; | |
return Ok(Literal(str)) | |
}, | |
None => return Err(UnterminatedStringLiteral) | |
} | |
}, | |
Some(ch) => { | |
// TODO: Detect digits | |
// Skip whitespace | |
if ch.is_whitespace() { | |
self.rest = s; | |
continue | |
} | |
/*// Since we've exhausted all other possibilities, this must | |
// be an atom. Unlike the quoted case, it's not an error to | |
// encounter EOF before whitespace. | |
let mut end_ch = None; | |
let str = { | |
let mut iter = self.rest.splitn(1, |ch: char| { | |
let term = ch == ')' || ch == '(' || ch.is_whitespace(); | |
if term { end_ch = Some(ch) } | |
term | |
}); | |
let str = match iter.next() { | |
Some(s) => s, | |
None => return Err(InternalError) | |
}; | |
self.rest = iter.next().unwrap_or(""); | |
str | |
}; | |
match end_ch { | |
Some(ch) => self.first = end_ch, | |
None => { | |
let (first, rest) = self.rest.slice_shift_char(); | |
self.first = first; | |
self.rest = rest; | |
} | |
}*/ | |
// Since we've exhausted all other possibilities, this must | |
// be an atom. Unlike the quoted case, it's not an error to | |
// encounter EOF before whitespace. | |
let mut end_ch = ch; | |
let mut iter = self.rest.splitn(1, |ch: char| { | |
end_ch = ch; | |
ch == ')' || ch == '(' || ch.is_whitespace() | |
}); | |
let str = match iter.next() { | |
Some(s) => s, | |
None => return Err(InternalError) | |
}; | |
self.rest = iter.next().unwrap_or(""); | |
return Ok(Literal(str)); | |
} | |
None => return Ok(EOF) | |
} | |
} | |
} | |
} | |
fn from_io_result<T>(res: io::IoResult<T>) -> Result<T, Error> { | |
res.map_err( |err| IoError(err) ) | |
} | |
impl<'a> SExp<'a> { | |
pub fn encode<T: io::Writer>(&self, writer: &mut T) -> Result<(), Error> { | |
match *self { | |
F64(f) => match f.classify() { | |
num::FPNormal | num::FPZero => from_io_result(write!(writer, "{}", f)), | |
_ => Err(NoReprForFloat) | |
}, | |
List(ref l) => { | |
try!(from_io_result(writer.write_char('('))) | |
let mut iter = l.iter(); | |
match iter.next() { | |
Some(sexp) => { | |
try!(sexp.encode(writer)); | |
for sexp in iter { | |
try!(from_io_result(writer.write_char(' '))); | |
try!(sexp.encode(writer)); | |
} | |
}, | |
None => (), | |
} | |
from_io_result(writer.write_char(')')) | |
}, | |
Str(s) => from_io_result(write!(writer, "\"{}\"", s)), | |
} | |
} | |
pub fn parse(string: &str) -> Result<SExp, Error> { | |
let arena = TypedArena::new(); | |
let mut tokens = Tokens::new(string); | |
let next = tokens.next(); | |
let mut list = match try!(next) { | |
ListStart => Vec::<SExp>::new(), | |
Literal(s) => if try!(tokens.next()) == EOF { return Ok(Str(s)) } else { return Err(ExpectedEOF) }, | |
ListEnd => return Err(IncorrectCloseDelimiter), | |
EOF => return Err(UnexpectedEOF), | |
}; | |
// We know we're in a list if we got this far. | |
let mut stack = Vec::new(); | |
loop { | |
let tok = tokens.next(); | |
println!("{} ({})", tok, ::std::mem::size_of::<Token>()); | |
match try!(tok) { | |
ListStart => { | |
stack.push(list); | |
list = Vec::new() | |
}, | |
Literal(s) => list.push(Str(s)), | |
ListEnd => match stack.pop() { | |
Some(mut l) => { | |
l.push(List(arena.alloc(list)[])); | |
println!("{}", l); | |
list = l; | |
}, | |
None => return Err(StackUnderflow), | |
}, | |
EOF => return Ok(F64(0.0)), | |
} | |
} | |
} | |
pub fn buffer_encode(&self) -> Result<String, Error> { | |
let mut m = io::MemWriter::new(); | |
try!(self.encode(&mut m)); | |
// Because encode() only ever writes valid UTF-8, we can safely skip the secondary check we | |
// normally have to do when converting from Vec<u8> to String. If we didn't know that the | |
// buffer was already UTF-8, we'd want to call container_as_str() here. | |
unsafe { Ok(string::raw::from_utf8(m.unwrap())) } | |
} | |
} | |
static sexp_struct: SExp<'static> = List([ | |
List([Str("data"), Str("quoted data"), F64(123.), F64(4.5)]), | |
List([List([Str("!@#"), List([F64(4.5)]), Str("(more"), Str("data)")])]), | |
]); | |
static sexp_string: &'static str = r#"(("data" "quoted data" 123 4.5) (("!@#" (4.5) "(more" "data)")))"#; | |
fn try_encode() -> Result<String, Error> { | |
sexp_struct.buffer_encode() | |
} | |
fn try_decode<'a>() -> Result<SExp<'a>, Error> { | |
SExp::parse(sexp_string) | |
} | |
#[cfg(not(test))] | |
fn main() { | |
println!("{}", try_encode()); | |
println!("{}", try_decode()); | |
} | |
#[test] | |
fn test() { | |
assert_eq!(Ok(sexp_string), try_encode().as_ref().map( |s| s[])); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment