Created
December 9, 2013 20:21
-
-
Save agrif/7880109 to your computer and use it in GitHub Desktop.
This file contains hidden or 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(macro_rules)]; | |
| use std::str; | |
| use std::io; | |
| use std::vec; | |
| // shim because Trait::<for T>::foo isn't implemented yet | |
| fn hintfunc<T>(x: T) -> T { x } | |
| macro_rules! hint( | |
| ($e:expr :: $t:ty) => (hintfunc::<$t>($e)) | |
| ) | |
| #[deriving(Eq,FromPrimitive,ToStr)] | |
| enum TagType { | |
| End = 0, | |
| Byte = 1, | |
| Short = 2, | |
| Int = 3, | |
| Long = 4, | |
| Float = 5, | |
| Double = 6, | |
| ByteArray = 7, | |
| String = 8, | |
| List = 9, | |
| Compound = 10, | |
| IntArray = 11 | |
| } | |
| impl TagType { | |
| fn to_pretty(&self) -> ~str { | |
| match *self { | |
| End => ~"TAG_End", | |
| Byte => ~"TAG_Byte", | |
| Short => ~"TAG_Short", | |
| Int => ~"TAG_Int", | |
| Long => ~"TAG_Long", | |
| Float => ~"TAG_Float", | |
| Double => ~"TAG_Double", | |
| ByteArray => ~"TAG_Byte_Array", | |
| String => ~"TAG_String", | |
| List => ~"TAG_List", | |
| Compound => ~"TAG_Compound", | |
| IntArray => ~"TAG_Int_Array" | |
| } | |
| } | |
| } | |
| trait Tag<N: TagName> { | |
| fn get_type(&self) -> TagType; | |
| fn get_name_ptr<'a>(&'a self) -> &'a N; | |
| fn pretty_print_part(&self, writer: &mut Writer, indent: |&mut Writer|); | |
| fn read(reader: &mut Reader) -> ~Self; | |
| } | |
| trait NamedTag { | |
| fn get_name<'a>(&'a self) -> &'a str; | |
| } | |
| impl NamedTag for ~Tag<~str> { | |
| fn get_name<'a>(&'a self) -> &'a str { | |
| (*self.get_name_ptr()).as_slice() | |
| } | |
| } | |
| trait PrettyPrintTag { | |
| fn pretty_print(&self, writer: &mut Writer, indent: |&mut Writer|); | |
| } | |
| impl PrettyPrintTag for ~Tag<~str> { | |
| fn pretty_print(&self, writer: &mut Writer, indent: |&mut Writer|) { | |
| indent(writer); | |
| write!(writer, "{}(\"{}\"): ", self.get_type().to_pretty(), self.get_name()); | |
| self.pretty_print_part(writer, indent); | |
| write!(writer, "\n"); | |
| } | |
| } | |
| impl<T: Tag<()>> PrettyPrintTag for ~T { | |
| fn pretty_print(&self, writer: &mut Writer, indent: |&mut Writer|) { | |
| indent(writer); | |
| write!(writer, "{}: ", self.get_type().to_pretty()); | |
| self.pretty_print_part(writer, indent); | |
| write!(writer, "\n"); | |
| } | |
| } | |
| trait TagName : Send { | |
| fn read(reader: &mut Reader) -> Self; | |
| } | |
| impl TagName for () { | |
| fn read(_: &mut Reader) {} | |
| } | |
| impl TagName for ~str { | |
| fn read(reader: &mut Reader) -> ~str { | |
| let size = reader.read_be_u16(); | |
| let name = reader.read_bytes(size as uint); | |
| return str::from_utf8_owned(name); | |
| } | |
| } | |
| struct EndTag { | |
| name: () | |
| } | |
| impl Tag<()> for EndTag { | |
| fn get_type(&self) -> TagType { End } | |
| fn get_name_ptr<'a>(&'a self) -> &'a () { &(self.name) } | |
| fn pretty_print_part(&self, _: &mut Writer, _: |&mut Writer|) {} | |
| fn read(_: &mut Reader) -> ~EndTag { ~EndTag { name: () } } | |
| } | |
| macro_rules! tag( | |
| ($name:ident(id=$tagtype:ident, value=$t:ty) { | |
| fn pretty_print_part(&self, $printervar:ident) $print:expr | |
| fn read($readervar:ident) $read:expr | |
| }) => ( | |
| struct $name<N> { | |
| name: N, | |
| value: $t | |
| } | |
| impl<N: TagName> Tag<N> for $name<N> { | |
| fn get_type(&self) -> TagType { $tagtype } | |
| fn get_name_ptr<'a>(&'a self) -> &'a N { &(self.name) } | |
| fn pretty_print_part(&self, $printervar: &mut Writer, _: |&mut Writer|) { | |
| } | |
| fn read(reader: &mut Reader) -> ~$name<N> { | |
| return ~$name { | |
| name: TagName::read(reader), | |
| value: $read | |
| }; | |
| } | |
| } | |
| ); | |
| ($name:ident(id=$tagtype:ident, value=$t:ty) = {$reader:ident}) => ( | |
| tag!($name(id=$tagtype, value=$t) { | |
| fn pretty_print_part(&self, writer) { | |
| write!(writer, "{}", self.value); | |
| } | |
| fn read(reader) { | |
| reader.$reader() | |
| } | |
| }) | |
| ); | |
| ) | |
| tag!(ByteTag(id=Byte, value=i8) = {read_i8}) | |
| tag!(ShortTag(id=Short, value=i16) = {read_be_i16}) | |
| tag!(IntTag(id=Int, value=i32) = {read_be_i32}) | |
| tag!(LongTag(id=Long, value=i64) = {read_be_i64}) | |
| tag!(FloatTag(id=Float, value=f32) = {read_be_f32}) | |
| tag!(DoubleTag(id=Double, value=f64) = {read_be_f64}) | |
| tag!(ByteArrayTag(id=ByteArray, value=~[u8]) { | |
| fn pretty_print_part(&self, writer) { | |
| write!(writer, "[{0, plural, one{# byte} other{# bytes}}]", self.value.len()); | |
| } | |
| fn read(reader) { | |
| let size = reader.read_be_u32(); | |
| reader.read_bytes(size as uint) | |
| } | |
| }) | |
| tag!(StringTag(id=String, value=~str) { | |
| fn pretty_print_part(&self, writer) { | |
| write!(writer, "{}", self.value); | |
| } | |
| fn read(reader) { | |
| let size = reader.read_be_u16(); | |
| let bytes = reader.read_bytes(size as uint); | |
| str::from_utf8_owned(bytes) | |
| } | |
| }) | |
| struct ListTag<N, T> { | |
| name: N, | |
| value: ~[~T] | |
| } | |
| impl<N: TagName, T: Tag<()>> Tag<N> for ListTag<N, T> { | |
| fn get_type(&self) -> TagType { List } | |
| fn get_name_ptr<'a>(&'a self) -> &'a N { &(self.name) } | |
| fn pretty_print_part(&self, writer: &mut Writer, indent: |&mut Writer|) { | |
| let mut subtype = End; | |
| if (self.value.len() > 0) { | |
| subtype = self.value[0].get_type(); | |
| } | |
| write!(writer, "{0, plural, one{# entry} other{# entries}} of type {1}\n", self.value.len(), subtype.to_pretty()); | |
| indent(writer); | |
| write!(writer, "{}\n", "{"); | |
| for t in self.value.iter() { | |
| t.pretty_print(writer, |w| { write!(w, " "); indent(w); }); | |
| } | |
| indent(writer); | |
| write!(writer, "{}", "}"); | |
| } | |
| fn read(reader: &mut Reader) -> ~ListTag<N, T> { | |
| let name = TagName::read(reader); | |
| let size = reader.read_be_u32(); | |
| return ~ListTag { | |
| name: name, | |
| value: vec::from_fn(size as uint, |_| Tag::read(reader)) | |
| }; | |
| } | |
| } | |
| struct CompoundTag<N> { | |
| name: N, | |
| value: ~[~Tag<~str>] | |
| } | |
| impl<N: TagName> Tag<N> for CompoundTag<N> { | |
| fn get_type(&self) -> TagType { Compound } | |
| fn get_name_ptr<'a>(&'a self) -> &'a N { &self.name } | |
| fn pretty_print_part(&self, writer: &mut Writer, indent: |&mut Writer|) { | |
| write!(writer, "{0, plural, one{# entry} other{# entries}}\n", self.value.len()); | |
| indent(writer); | |
| write!(writer, "{}\n", "{"); | |
| for t in self.value.iter() { | |
| t.pretty_print(writer, |w| { write!(w, " "); indent(w); }); | |
| } | |
| indent(writer); | |
| write!(writer, "{}", "}"); | |
| } | |
| fn read(reader: &mut Reader) -> ~CompoundTag<N> { | |
| let name = TagName::read(reader); | |
| let mut gather = ~[]; | |
| loop { | |
| match read(reader) { | |
| Some(tag) => gather.push(tag), | |
| None => break | |
| } | |
| } | |
| return ~CompoundTag { | |
| name: name, | |
| value: gather | |
| }; | |
| } | |
| } | |
| tag!(IntArrayTag(id=IntArray, value=~[i32]) { | |
| fn pretty_print_part(&self, writer) { | |
| write!(writer, "[{0, plural, one{# int} other{# ints}}]", self.value.len()); | |
| } | |
| fn read(reader) { | |
| let size = reader.read_be_u32(); | |
| vec::from_fn(size as uint, |_| reader.read_be_i32()) | |
| } | |
| }) | |
| fn read<N: TagName>(reader: &mut Reader) -> Option<~Tag<N>> { | |
| let t: Option<TagType> = FromPrimitive::from_u8(reader.read_u8()); | |
| match t { | |
| Some(End) => None, | |
| Some(Byte) => | |
| Some(hint!(Tag::read(reader) :: ~ByteTag<N>) as ~Tag<N>), | |
| Some(Short) => | |
| Some(hint!(Tag::read(reader) :: ~ShortTag<N>) as ~Tag<N>), | |
| Some(Int) => | |
| Some(hint!(Tag::read(reader) :: ~IntTag<N>) as ~Tag<N>), | |
| Some(Long) => | |
| Some(hint!(Tag::read(reader) :: ~LongTag<N>) as ~Tag<N>), | |
| Some(Float) => | |
| Some(hint!(Tag::read(reader) :: ~FloatTag<N>) as ~Tag<N>), | |
| Some(Double) => | |
| Some(hint!(Tag::read(reader) :: ~DoubleTag<N>) as ~Tag<N>), | |
| Some(ByteArray) => | |
| Some(hint!(Tag::read(reader) :: ~ByteArrayTag<N>) as ~Tag<N>), | |
| Some(String) => | |
| Some(hint!(Tag::read(reader) :: ~StringTag<N>) as ~Tag<N>), | |
| Some(List) => { | |
| let name: N = TagName::read(reader); | |
| let st: Option<TagType> = FromPrimitive::from_u8(reader.read_u8()); | |
| match st { | |
| Some(End) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), EndTag>).value} as ~Tag<N>), | |
| Some(Byte) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), ByteTag<()>>).value} as ~Tag<N>), | |
| Some(Short) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), ShortTag<()>>).value} as ~Tag<N>), | |
| Some(Int) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), IntTag<()>>).value} as ~Tag<N>), | |
| Some(Long) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), LongTag<()>>).value} as ~Tag<N>), | |
| Some(Float) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), FloatTag<()>>).value} as ~Tag<N>), | |
| Some(Double) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), DoubleTag<()>>).value} as ~Tag<N>), | |
| Some(ByteArray) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), ByteArrayTag<()>>).value} as ~Tag<N>), | |
| Some(String) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), StringTag<()>>).value} as ~Tag<N>), | |
| Some(List) => None, // FIXME impossible with the current system | |
| Some(Compound) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), CompoundTag<()>>).value} as ~Tag<N>), | |
| Some(IntArray) => | |
| Some(~ListTag{name: name, value: hint!(Tag::read(reader) :: ~ListTag<(), IntArrayTag<()>>).value} as ~Tag<N>), | |
| None => None // FIXME | |
| } | |
| }, | |
| Some(Compound) => | |
| Some(hint!(Tag::read(reader) :: ~CompoundTag<N>) as ~Tag<N>), | |
| Some(IntArray) => | |
| Some(hint!(Tag::read(reader) :: ~IntArrayTag<N>) as ~Tag<N>), | |
| None => None // FIXME | |
| } | |
| } | |
| fn main() { | |
| let mut reader = io::stdin(); | |
| let mut writer = io::stdout(); | |
| let tag: ~Tag<~str> = read(&mut reader as &mut Reader).unwrap(); | |
| tag.pretty_print(&mut writer as &mut Writer, |_| ()); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment